Remove support for mxDateTime

The mxDateTime support has a number of issues:

- The mxDateTime is Python 2 only. There is no support for Python 3 nor
  talk of adding it AFAICT.

- Package mxDateTime support is not tested by Travis CI. New changes,
  especially around date/time interactions, could break it.

- Running psycopg2 tests with mxDateTime support results in the failures
  below.

- Installing egenix-mx-base (to install mxDateTime) with pip fails with
  the error:

    gcc: error: mx/DateTime/mxDateTime/mxDateTime.c: No such file or directory

- All of this to say, it looks like the mxDateTime package is
  unmaintained. It hasn't had a release since 2015.

The datetime module has long been a part of the Python stdlib. All
psycopg2 supported Python environments have date/time support, so
mxDateTime may not be as necessary as it was in the past.

Given the above, I recommend removing mxDateTime support. There are a
number of other items being removed with 2.8, so it is a good
opportunity for backwards incompatible changes.

Alternatively, we could look at:

1. Deprecating mxDateTime for a period (perhaps until Python 2 support is
  dropped).

Or

1. Fixing the unit test failures
2. Find a workaround for the pip install failure
3. Running mxDateTime as part of Travis

---

Failures:

======================================================================
ERROR: test_iter_named_cursor_efficient (tests.test_cursor.CursorTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/testutils.py", line 433, in slow_
    return f(self)
  File "tests/testutils.py", line 282, in skip_before_postgres__
    return f(self)
  File "tests/test_cursor.py", line 349, in test_iter_named_cursor_efficient
    self.assert_((t2 - t1).microseconds * 1e-6 < 0.1,
AttributeError: microseconds

======================================================================
ERROR: test_adapt_infinity_tz (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 405, in test_adapt_infinity_tz
    self.assert_(t.tzinfo is None)
AttributeError: tzinfo

======================================================================
ERROR: test_interval_overflow (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 396, in test_interval_overflow
    self.assertRaises(OverflowError, f, '100000000000000000:00:00')
  File "/usr/lib64/python2.7/unittest/case.py", line 511, in assertRaises
    callableObj(*args, **kwargs)
  File "tests/test_dates.py", line 394, in f
    return cur.fetchone()[0]
RangeError: DateTimeDelta value out of range

======================================================================
ERROR: test_large_interval (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/testutils.py", line 282, in skip_before_postgres__
    return f(self)
  File "tests/test_dates.py", line 350, in test_large_interval
    self.assertEqual(total_seconds(t), 999999 * 60 * 60)
  File "tests/test_dates.py", line 34, in total_seconds
    return d.days * 24 * 60 * 60 + d.seconds + d.microseconds / 1000000.0
AttributeError: microseconds

======================================================================
ERROR: test_micros_rounding (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 369, in test_micros_rounding
    self.assertEqual(total_seconds(t), 0.1)
  File "tests/test_dates.py", line 34, in total_seconds
    return d.days * 24 * 60 * 60 + d.seconds + d.microseconds / 1000000.0
AttributeError: microseconds

======================================================================
ERROR: test_time_value_error_sec_59_99 (tests.test_dates.FromTicksTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 647, in test_time_value_error_sec_59_99
    self.assertEqual(s.adapted.replace(hour=0),
AttributeError: replace

======================================================================
FAIL: test_interval_iso_8601_not_supported (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/testutils.py", line 282, in skip_before_postgres__
    return f(self)
  File "tests/test_dates.py", line 447, in test_interval_iso_8601_not_supported
    self.assertRaises(psycopg2.NotSupportedError, cur.fetchone)
AssertionError: NotSupportedError not raised

======================================================================
FAIL: test_redshift_day (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 439, in test_redshift_day
    self.assertEqual(r, v, "%s -> %s != %s" % (s, r, v))
AssertionError: 1 -> 00:00:00.00 != 0:00:00.000001

======================================================================
FAIL: test_type_roundtrip_date (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 279, in test_type_roundtrip_date
    self._test_type_roundtrip(date(2010, 5, 3))
  File "tests/test_dates.py", line 269, in _test_type_roundtrip
    self.assertEqual(type(o1), type(o2))
AssertionError: <type 'datetime.date'> != <type 'mx.DateTime.DateTime'>

======================================================================
FAIL: test_type_roundtrip_date_array (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 315, in test_type_roundtrip_date_array
    self._test_type_roundtrip_array(date(2010, 5, 3))
  File "tests/test_dates.py", line 275, in _test_type_roundtrip_array
    self.assertEqual(type(o1[0]), type(o2[0]))
AssertionError: <type 'datetime.date'> != <type 'mx.DateTime.DateTime'>

======================================================================
FAIL: test_type_roundtrip_datetime (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 283, in test_type_roundtrip_datetime
    dt = self._test_type_roundtrip(datetime(2010, 5, 3, 10, 20, 30))
  File "tests/test_dates.py", line 269, in _test_type_roundtrip
    self.assertEqual(type(o1), type(o2))
AssertionError: <type 'datetime.datetime'> != <type 'mx.DateTime.DateTime'>

======================================================================
FAIL: test_type_roundtrip_datetime_array (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 319, in test_type_roundtrip_datetime_array
    self._test_type_roundtrip_array(datetime(2010, 5, 3, 10, 20, 30))
  File "tests/test_dates.py", line 275, in _test_type_roundtrip_array
    self.assertEqual(type(o1[0]), type(o2[0]))
AssertionError: <type 'datetime.datetime'> != <type 'mx.DateTime.DateTime'>

======================================================================
FAIL: test_type_roundtrip_datetimetz (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 291, in test_type_roundtrip_datetimetz
    dt2 = self._test_type_roundtrip(dt1)
  File "tests/test_dates.py", line 269, in _test_type_roundtrip
    self.assertEqual(type(o1), type(o2))
AssertionError: <type 'datetime.datetime'> != <type 'mx.DateTime.DateTime'>

======================================================================
FAIL: test_type_roundtrip_datetimetz_array (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 324, in test_type_roundtrip_datetimetz_array
    datetime(2010, 5, 3, 10, 20, 30, tzinfo=FixedOffsetTimezone(0)))
  File "tests/test_dates.py", line 275, in _test_type_roundtrip_array
    self.assertEqual(type(o1[0]), type(o2[0]))
AssertionError: <type 'datetime.datetime'> != <type 'mx.DateTime.DateTime'>

======================================================================
FAIL: test_type_roundtrip_interval (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 311, in test_type_roundtrip_interval
    self._test_type_roundtrip(timedelta(seconds=30))
  File "tests/test_dates.py", line 269, in _test_type_roundtrip
    self.assertEqual(type(o1), type(o2))
AssertionError: <type 'datetime.timedelta'> != <type 'mx.DateTime.DateTimeDelta'>

======================================================================
FAIL: test_type_roundtrip_interval_array (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 332, in test_type_roundtrip_interval_array
    self._test_type_roundtrip_array(timedelta(seconds=30))
  File "tests/test_dates.py", line 275, in _test_type_roundtrip_array
    self.assertEqual(type(o1[0]), type(o2[0]))
AssertionError: <type 'datetime.timedelta'> != <type 'mx.DateTime.DateTimeDelta'>

======================================================================
FAIL: test_type_roundtrip_time (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 297, in test_type_roundtrip_time
    tm = self._test_type_roundtrip(time(10, 20, 30))
  File "tests/test_dates.py", line 269, in _test_type_roundtrip
    self.assertEqual(type(o1), type(o2))
AssertionError: <type 'datetime.time'> != <type 'mx.DateTime.DateTimeDelta'>

======================================================================
FAIL: test_type_roundtrip_time_array (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 328, in test_type_roundtrip_time_array
    self._test_type_roundtrip_array(time(10, 20, 30))
  File "tests/test_dates.py", line 275, in _test_type_roundtrip_array
    self.assertEqual(type(o1[0]), type(o2[0]))
AssertionError: <type 'datetime.time'> != <type 'mx.DateTime.DateTimeDelta'>

======================================================================
FAIL: test_type_roundtrip_timetz (tests.test_dates.DatetimeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 305, in test_type_roundtrip_timetz
    tm2 = self._test_type_roundtrip(tm1)
  File "tests/test_dates.py", line 269, in _test_type_roundtrip
    self.assertEqual(type(o1), type(o2))
AssertionError: <type 'datetime.time'> != <type 'mx.DateTime.DateTimeDelta'>

======================================================================
FAIL: test_date_value_error_sec_59_99 (tests.test_dates.FromTicksTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 642, in test_date_value_error_sec_59_99
    self.assert_(s.adapted in [date(2010, 5, 6), date(2010, 5, 7)])
AssertionError: False is not true

======================================================================
FAIL: test_timestamp_value_error_sec_59_99 (tests.test_dates.FromTicksTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/test_dates.py", line 636, in test_timestamp_value_error_sec_59_99
    tzinfo=FixedOffsetTimezone(-5 * 60)))
AssertionError: <mx.DateTime.DateTime object for '2010-05-06 12:11:59.99' at 7fcbb03cc348> != datetime.datetime(2010, 5, 6, 14, 11, 59, 999920, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=-300, name=None))

======================================================================
FAIL: test_adapt_date_range (tests.test_types_extras.RangeCasterTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/testutils.py", line 282, in skip_before_postgres__
    return f(self)
  File "tests/test_types_extras.py", line 1627, in test_adapt_date_range
    self.assertEqual(r1.lower, ts1)
AssertionError: <mx.DateTime.DateTime object for '1999-12-31 06:00:00.00' at 7fcbb03cc300> != datetime.datetime(2000, 1, 1, 0, 0, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=600, name=None))

======================================================================
FAIL: test_cast_timestamptz (tests.test_types_extras.RangeCasterTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "tests/testutils.py", line 282, in skip_before_postgres__
    return f(self)
  File "tests/test_types_extras.py", line 1535, in test_cast_timestamptz
    self.assertEqual(r.lower, ts1)
AssertionError: <mx.DateTime.DateTime object for '1999-12-31 06:00:00.00' at 7fcbb03cc348> != datetime.datetime(2000, 1, 1, 0, 0, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=600, name=None))
This commit is contained in:
Jon Dufresne 2019-03-13 20:03:31 -07:00
parent ad4c6a4673
commit 6df60ad588
18 changed files with 8 additions and 1184 deletions

1
NEWS
View File

@ -49,6 +49,7 @@ Other changes:
- The `!psycopg2.test` package is no longer installed by ``python setup.py
install``.
- Wheel package compiled against OpenSSL 1.0.2r and PostgreSQL 11.2 libpq.
- Dropped support for `mxDateTime`.
What's new in psycopg 2.7.7

View File

@ -121,8 +121,6 @@ rst_epilog = """
.. _transaction isolation level:
https://www.postgresql.org/docs/current/static/transaction-iso.html
.. _mx.DateTime: https://www.egenix.com/products/python/mxBase/mxDateTime/
.. |MVCC| replace:: :abbr:`MVCC (Multiversion concurrency control)`
"""

View File

@ -453,13 +453,6 @@ deal with Python objects adaptation:
Specialized adapters for Python datetime objects.
.. class:: DateFromMx
TimeFromMx
TimestampFromMx
IntervalFromMx
Specialized adapters for `mx.DateTime`_ objects.
.. data:: adapters
Dictionary of the currently registered object adapters. Use
@ -1004,25 +997,11 @@ from the database. See :ref:`unicode-handling` for details.
Typecasters to convert time-related data types to Python `!datetime`
objects.
.. data:: MXDATE
MXDATETIME
MXDATETIMETZ
MXINTERVAL
MXTIME
MXDATEARRAY
MXDATETIMEARRAY
MXDATETIMETZARRAY
MXINTERVALARRAY
MXTIMEARRAY
Typecasters to convert time-related data types to `mx.DateTime`_ objects.
Only available if Psycopg was compiled with `!mx` support.
.. versionchanged:: 2.2
previously the `DECIMAL` typecaster and the specific time-related
typecasters (`!PY*` and `!MX*`) were not exposed by the `extensions`
module. In older versions they can be imported from the implementation
module `!psycopg2._psycopg`.
previously the `DECIMAL` typecaster and the time-related `!PY*`
typecasters were not exposed by the `extensions` module. In older
versions they can be imported from the implementation module
`!psycopg2._psycopg`.
.. versionadded:: 2.7.2
the `!*DATETIMETZ*` objects.

View File

@ -221,7 +221,6 @@ If you have less standard requirements such as:
- creating a :ref:`debug build <debug-build>`,
- using :program:`pg_config` not in the :envvar:`PATH`,
- supporting ``mx.DateTime``,
then take a look at the ``setup.cfg`` file.

View File

@ -540,7 +540,6 @@ or `!memoryview` (in Python 3).
single: Date objects; Adaptation
single: Time objects; Adaptation
single: Interval objects; Adaptation
single: mx.DateTime; Adaptation
.. _adapt-date:
@ -550,8 +549,7 @@ Date/Time objects adaptation
Python builtin `~datetime.datetime`, `~datetime.date`,
`~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's
:sql:`timestamp[tz]`, :sql:`date`, :sql:`time[tz]`, :sql:`interval` data types.
Time zones are supported too. The Egenix `mx.DateTime`_ objects are adapted
the same way::
Time zones are supported too.
>>> dt = datetime.datetime.now()
>>> dt

View File

@ -41,14 +41,6 @@ from psycopg2._psycopg import ( # noqa
ROWIDARRAY, STRINGARRAY, TIME, TIMEARRAY, UNICODE, UNICODEARRAY,
AsIs, Binary, Boolean, Float, Int, QuotedString, )
try:
from psycopg2._psycopg import ( # noqa
MXDATE, MXDATETIME, MXDATETIMETZ, MXINTERVAL, MXTIME, MXDATEARRAY,
MXDATETIMEARRAY, MXDATETIMETZARRAY, MXINTERVALARRAY, MXTIMEARRAY,
DateFromMx, TimeFromMx, TimestampFromMx, IntervalFromMx, )
except ImportError:
pass
try:
from psycopg2._psycopg import ( # noqa
PYDATE, PYDATETIME, PYDATETIMETZ, PYINTERVAL, PYTIME, PYDATEARRAY,

View File

@ -265,8 +265,6 @@ PyTypeObject pydatetimeType = {
/** module-level functions **/
#ifdef PSYCOPG_DEFAULT_PYDATETIME
PyObject *
psyco_Date(PyObject *self, PyObject *args)
{
@ -458,8 +456,6 @@ exit:
return res;
}
#endif
PyObject *
psyco_DateFromPy(PyObject *self, PyObject *args)
{

View File

@ -48,7 +48,6 @@ typedef struct {
RAISES_NEG HIDDEN int psyco_adapter_datetime_init(void);
/* functions exported to psycopgmodule.c */
#ifdef PSYCOPG_DEFAULT_PYDATETIME
HIDDEN PyObject *psyco_Date(PyObject *module, PyObject *args);
#define psyco_Date_doc \
@ -86,8 +85,6 @@ HIDDEN PyObject *psyco_TimestampFromTicks(PyObject *module, PyObject *args);
"Ticks are the number of seconds since the epoch; see the documentation " \
"of the standard Python time module for details)."
#endif /* PSYCOPG_DEFAULT_PYDATETIME */
HIDDEN PyObject *psyco_DateFromPy(PyObject *module, PyObject *args);
#define psyco_DateFromPy_doc \
"DateFromPy(datetime.date) -> new wrapper"

View File

@ -1,428 +0,0 @@
/* adapter_mxdatetime.c - mx date/time objects
*
* Copyright (C) 2003-2019 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#define PSYCOPG_MODULE
#include "psycopg/psycopg.h"
#include "psycopg/adapter_mxdatetime.h"
#include "psycopg/microprotocols_proto.h"
#include <mxDateTime.h>
#include <string.h>
/* Return 0 on success, -1 on failure, but don't set an exception */
int
psyco_adapter_mxdatetime_init(void)
{
Dprintf("psyco_adapter_mxdatetime_init: mx.DateTime init");
if (mxDateTime_ImportModuleAndAPI()) {
Dprintf("psyco_adapter_mxdatetime_init: mx.DateTime initialization failed");
PyErr_Clear();
return -1;
}
return 0;
}
/* mxdatetime_str, mxdatetime_getquoted - return result of quoting */
static PyObject *
mxdatetime_str(mxdatetimeObject *self)
{
mxDateTimeObject *dt;
mxDateTimeDeltaObject *dtd;
char buf[128] = { 0, };
switch (self->type) {
case PSYCO_MXDATETIME_DATE:
dt = (mxDateTimeObject *)self->wrapped;
if (dt->year >= 1)
PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d'::date",
dt->year, (int)dt->month, (int)dt->day);
else
PyOS_snprintf(buf, sizeof(buf) - 1, "'%04ld-%02d-%02d BC'::date",
1 - dt->year, (int)dt->month, (int)dt->day);
break;
case PSYCO_MXDATETIME_TIMESTAMP:
dt = (mxDateTimeObject *)self->wrapped;
if (dt->year >= 1)
PyOS_snprintf(buf, sizeof(buf) - 1,
"'%04ld-%02d-%02dT%02d:%02d:%09.6f'::timestamp",
dt->year, (int)dt->month, (int)dt->day,
(int)dt->hour, (int)dt->minute, dt->second);
else
PyOS_snprintf(buf, sizeof(buf) - 1,
"'%04ld-%02d-%02dT%02d:%02d:%09.6f BC'::timestamp",
1 - dt->year, (int)dt->month, (int)dt->day,
(int)dt->hour, (int)dt->minute, dt->second);
break;
case PSYCO_MXDATETIME_TIME:
case PSYCO_MXDATETIME_INTERVAL:
/* given the limitation of the mx.DateTime module that uses the same
type for both time and delta values we need to do some black magic
and make sure we're not using an adapt()ed interval as a simple
time */
dtd = (mxDateTimeDeltaObject *)self->wrapped;
if (0 <= dtd->seconds && dtd->seconds < 24*3600) {
PyOS_snprintf(buf, sizeof(buf) - 1, "'%02d:%02d:%09.6f'::time",
(int)dtd->hour, (int)dtd->minute, dtd->second);
} else {
double ss = dtd->hour*3600.0 + dtd->minute*60.0 + dtd->second;
if (dtd->seconds >= 0)
PyOS_snprintf(buf, sizeof(buf) - 1, "'%ld days %.6f seconds'::interval",
dtd->day, ss);
else
PyOS_snprintf(buf, sizeof(buf) - 1, "'-%ld days -%.6f seconds'::interval",
dtd->day, ss);
}
break;
}
return PyString_FromString(buf);
}
static PyObject *
mxdatetime_getquoted(mxdatetimeObject *self, PyObject *args)
{
return mxdatetime_str(self);
}
static PyObject *
mxdatetime_conform(mxdatetimeObject *self, PyObject *args)
{
PyObject *res, *proto;
if (!PyArg_ParseTuple(args, "O", &proto)) return NULL;
if (proto == (PyObject*)&isqlquoteType)
res = (PyObject*)self;
else
res = Py_None;
Py_INCREF(res);
return res;
}
/** the MxDateTime object **/
/* object member list */
static struct PyMemberDef mxdatetimeObject_members[] = {
{"adapted", T_OBJECT, offsetof(mxdatetimeObject, wrapped), READONLY},
{"type", T_INT, offsetof(mxdatetimeObject, type), READONLY},
{NULL}
};
/* object method table */
static PyMethodDef mxdatetimeObject_methods[] = {
{"getquoted", (PyCFunction)mxdatetime_getquoted, METH_NOARGS,
"getquoted() -> wrapped object value as SQL date/time"},
{"__conform__", (PyCFunction)mxdatetime_conform, METH_VARARGS, NULL},
{NULL} /* Sentinel */
};
/* initialization and finalization methods */
static int
mxdatetime_setup(mxdatetimeObject *self, PyObject *obj, int type)
{
Dprintf("mxdatetime_setup: init mxdatetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, Py_REFCNT(self)
);
self->type = type;
Py_INCREF(obj);
self->wrapped = obj;
Dprintf("mxdatetime_setup: good mxdatetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
self, Py_REFCNT(self)
);
return 0;
}
static void
mxdatetime_dealloc(PyObject* obj)
{
mxdatetimeObject *self = (mxdatetimeObject *)obj;
Py_CLEAR(self->wrapped);
Dprintf("mxdatetime_dealloc: deleted mxdatetime object at %p, refcnt = "
FORMAT_CODE_PY_SSIZE_T,
obj, Py_REFCNT(obj)
);
Py_TYPE(obj)->tp_free(obj);
}
static int
mxdatetime_init(PyObject *obj, PyObject *args, PyObject *kwds)
{
PyObject *mx;
int type = -1; /* raise an error if type was not passed! */
if (!PyArg_ParseTuple(args, "O|i", &mx, &type))
return -1;
return mxdatetime_setup((mxdatetimeObject *)obj, mx, type);
}
static PyObject *
mxdatetime_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
/* object type */
#define mxdatetimeType_doc \
"MxDateTime(mx, type) -> new mx.DateTime wrapper object"
PyTypeObject mxdatetimeType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2._psycopg.MxDateTime",
sizeof(mxdatetimeObject), 0,
mxdatetime_dealloc, /*tp_dealloc*/
0, /*tp_print*/
0, /*tp_getattr*/
0, /*tp_setattr*/
0, /*tp_compare*/
0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
0, /*tp_hash */
0, /*tp_call*/
(reprfunc)mxdatetime_str, /*tp_str*/
0, /*tp_getattro*/
0, /*tp_setattro*/
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE, /*tp_flags*/
mxdatetimeType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
mxdatetimeObject_methods, /*tp_methods*/
mxdatetimeObject_members, /*tp_members*/
0, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
mxdatetime_init, /*tp_init*/
0, /*tp_alloc*/
mxdatetime_new, /*tp_new*/
};
/** module-level functions **/
#ifdef PSYCOPG_DEFAULT_MXDATETIME
PyObject *
psyco_Date(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
int year, month, day;
if (!PyArg_ParseTuple(args, "iii", &year, &month, &day))
return NULL;
mx = mxDateTime.DateTime_FromDateAndTime(year, month, day, 0, 0, 0.0);
if (mx == NULL) return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_DATE);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_Time(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
int hours, minutes=0;
double seconds=0.0;
if (!PyArg_ParseTuple(args, "iid", &hours, &minutes, &seconds))
return NULL;
mx = mxDateTime.DateTimeDelta_FromTime(hours, minutes, seconds);
if (mx == NULL) return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIME);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_Timestamp(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
int year, month, day;
int hour=0, minute=0; /* default to midnight */
double second=0.0;
if (!PyArg_ParseTuple(args, "lii|iid", &year, &month, &day,
&hour, &minute, &second))
return NULL;
mx = mxDateTime.DateTime_FromDateAndTime(year, month, day,
hour, minute, second);
if (mx == NULL) return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIMESTAMP);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_DateFromTicks(PyObject *self, PyObject *args)
{
PyObject *res, *mx;
double ticks;
if (!PyArg_ParseTuple(args,"d", &ticks))
return NULL;
if (!(mx = mxDateTime.DateTime_FromTicks(ticks)))
return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_DATE);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_TimeFromTicks(PyObject *self, PyObject *args)
{
PyObject *res, *mx, *dt;
double ticks;
if (!PyArg_ParseTuple(args,"d", &ticks))
return NULL;
if (!(dt = mxDateTime.DateTime_FromTicks(ticks)))
return NULL;
if (!(mx = mxDateTime.DateTimeDelta_FromDaysAndSeconds(
0, ((mxDateTimeObject*)dt)->abstime)))
{
Py_DECREF(dt);
return NULL;
}
Py_DECREF(dt);
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIME);
Py_DECREF(mx);
return res;
}
PyObject *
psyco_TimestampFromTicks(PyObject *self, PyObject *args)
{
PyObject *mx, *res;
double ticks;
if (!PyArg_ParseTuple(args, "d", &ticks))
return NULL;
if (!(mx = mxDateTime.DateTime_FromTicks(ticks)))
return NULL;
res = PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIMESTAMP);
Py_DECREF(mx);
return res;
}
#endif
PyObject *
psyco_DateFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTime_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_DATE);
}
PyObject *
psyco_TimeFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTimeDelta_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIME);
}
PyObject *
psyco_TimestampFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTime_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_TIMESTAMP);
}
PyObject *
psyco_IntervalFromMx(PyObject *self, PyObject *args)
{
PyObject *mx;
if (!PyArg_ParseTuple(args, "O!", mxDateTime.DateTime_Type, &mx))
return NULL;
return PyObject_CallFunction((PyObject *)&mxdatetimeType, "Oi", mx,
PSYCO_MXDATETIME_INTERVAL);
}

View File

@ -1,98 +0,0 @@
/* adapter_mxdatetime.h - definition for the mx date/time types
*
* Copyright (C) 2003-2019 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#ifndef PSYCOPG_MXDATETIME_H
#define PSYCOPG_MXDATETIME_H 1
#ifdef __cplusplus
extern "C" {
#endif
extern HIDDEN PyTypeObject mxdatetimeType;
typedef struct {
PyObject_HEAD
PyObject *wrapped;
int type;
#define PSYCO_MXDATETIME_TIME 0
#define PSYCO_MXDATETIME_DATE 1
#define PSYCO_MXDATETIME_TIMESTAMP 2
#define PSYCO_MXDATETIME_INTERVAL 3
} mxdatetimeObject;
/* functions exported to psycopgmodule.c */
#ifdef PSYCOPG_DEFAULT_MXDATETIME
HIDDEN PyObject *psyco_Date(PyObject *module, PyObject *args);
#define psyco_Date_doc \
"Date(year, month, day) -> new date"
HIDDEN PyObject *psyco_Time(PyObject *module, PyObject *args);
#define psyco_Time_doc \
"Time(hour, minutes, seconds) -> new time"
HIDDEN PyObject *psyco_Timestamp(PyObject *module, PyObject *args);
#define psyco_Timestamp_doc \
"Time(year, month, day, hour, minutes, seconds) -> new timestamp"
HIDDEN PyObject *psyco_DateFromTicks(PyObject *module, PyObject *args);
#define psyco_DateFromTicks_doc \
"DateFromTicks(ticks) -> new date"
HIDDEN PyObject *psyco_TimeFromTicks(PyObject *module, PyObject *args);
#define psyco_TimeFromTicks_doc \
"TimeFromTicks(ticks) -> new time"
HIDDEN PyObject *psyco_TimestampFromTicks(PyObject *module, PyObject *args);
#define psyco_TimestampFromTicks_doc \
"TimestampFromTicks(ticks) -> new timestamp"
#endif /* PSYCOPG_DEFAULT_MXDATETIME */
HIDDEN int psyco_adapter_mxdatetime_init(void);
HIDDEN PyObject *psyco_DateFromMx(PyObject *module, PyObject *args);
#define psyco_DateFromMx_doc \
"DateFromMx(mx) -> new date"
HIDDEN PyObject *psyco_TimeFromMx(PyObject *module, PyObject *args);
#define psyco_TimeFromMx_doc \
"TimeFromMx(mx) -> new time"
HIDDEN PyObject *psyco_TimestampFromMx(PyObject *module, PyObject *args);
#define psyco_TimestampFromMx_doc \
"TimestampFromMx(mx) -> new timestamp"
HIDDEN PyObject *psyco_IntervalFromMx(PyObject *module, PyObject *args);
#define psyco_IntervalFromMx_doc \
"IntervalFromMx(mx) -> new interval"
#ifdef __cplusplus
}
#endif
#endif /* !defined(PSYCOPG_MXDATETIME_H) */

View File

@ -52,11 +52,6 @@
#include "psycopg/adapter_list.h"
#include "psycopg/typecast_binary.h"
#ifdef HAVE_MXDATETIME
#include <mxDateTime.h>
#include "psycopg/adapter_mxdatetime.h"
#endif
/* some module-level variables, like the datetime module */
#include <datetime.h>
#include "psycopg/adapter_datetime.h"
@ -376,30 +371,6 @@ adapters_init(PyObject *module)
if (0 > microprotocols_add(PyDateTimeAPI->DeltaType, NULL, obj)) { goto exit; }
Py_CLEAR(obj);
#ifdef HAVE_MXDATETIME
/* As above, we use the callable objects from the psycopg module.
These object are not be available at runtime if mx.DateTime import
failed (e.g. it was available at build time but not at runtime). */
if (PyMapping_HasKeyString(dict, "TimestampFromMx")) {
if (!(obj = PyMapping_GetItemString(dict, "TimestampFromMx"))) {
goto exit;
}
if (0 > microprotocols_add(mxDateTime.DateTime_Type, NULL, obj)) {
goto exit;
}
Py_CLEAR(obj);
/* if we found the above, we have this too. */
if (!(obj = PyMapping_GetItemString(dict, "TimeFromMx"))) {
goto exit;
}
if (0 > microprotocols_add(mxDateTime.DateTimeDelta_Type, NULL, obj)) {
goto exit;
}
Py_CLEAR(obj);
}
#endif
/* Success! */
rv = 0;
@ -958,41 +929,6 @@ datetime_init(void)
return 0;
}
RAISES_NEG static int
mxdatetime_init(PyObject *module)
{
Dprintf("psycopgmodule: initializing mx.DateTime module");
#ifdef HAVE_MXDATETIME
Py_TYPE(&mxdatetimeType) = &PyType_Type;
if (0 > PyType_Ready(&mxdatetimeType)) { return -1; }
if (mxDateTime_ImportModuleAndAPI()) {
Dprintf("psycopgmodule: mx.DateTime module import failed");
PyErr_Clear();
/* only fail if the mx typacaster should have been the default */
#ifdef PSYCOPG_DEFAULT_MXDATETIME
PyErr_SetString(PyExc_ImportError,
"can't import mx.DateTime module (requested as default adapter)");
return -1;
#endif
}
/* If we can't find mx.DateTime objects at runtime,
* remove them from the module (and, as consequence, from the adapters). */
if (0 != psyco_adapter_mxdatetime_init()) {
PyObject *dict;
if (!(dict = PyModule_GetDict(module))) { return -1; }
if (0 > PyDict_DelItemString(dict, "DateFromMx")) { return -1; }
if (0 > PyDict_DelItemString(dict, "TimeFromMx")) { return -1; }
if (0 > PyDict_DelItemString(dict, "TimestampFromMx")) { return -1; }
if (0 > PyDict_DelItemString(dict, "IntervalFromMx")) { return -1; }
}
#endif
return 0;
}
/** method table and module initialization **/
static PyMethodDef psycopgMethods[] = {
@ -1036,18 +972,6 @@ static PyMethodDef psycopgMethods[] = {
{"IntervalFromPy", (PyCFunction)psyco_IntervalFromPy,
METH_VARARGS, psyco_IntervalFromPy_doc},
#ifdef HAVE_MXDATETIME
/* to be deleted if not found at import time */
{"DateFromMx", (PyCFunction)psyco_DateFromMx,
METH_VARARGS, psyco_DateFromMx_doc},
{"TimeFromMx", (PyCFunction)psyco_TimeFromMx,
METH_VARARGS, psyco_TimeFromMx_doc},
{"TimestampFromMx", (PyCFunction)psyco_TimestampFromMx,
METH_VARARGS, psyco_TimestampFromMx_doc},
{"IntervalFromMx", (PyCFunction)psyco_IntervalFromMx,
METH_VARARGS, psyco_IntervalFromMx_doc},
#endif
{"set_wait_callback", (PyCFunction)psyco_set_wait_callback,
METH_O, psyco_set_wait_callback_doc},
{"get_wait_callback", (PyCFunction)psyco_get_wait_callback,
@ -1114,7 +1038,6 @@ INIT_MODULE(_psycopg)(void)
if (0 > add_module_constants(module)) { goto exit; }
if (0 > add_module_types(module)) { goto exit; }
if (0 > datetime_init()) { goto exit; }
if (0 > mxdatetime_init(module)) { goto exit; }
if (0 > encodings_init(module)) { goto exit; }
if (0 > typecast_init(module)) { goto exit; }
if (0 > adapters_init(module)) { goto exit; }

View File

@ -31,15 +31,6 @@
/* useful function used by some typecasters */
#ifdef HAVE_MXDATETIME
static const char *
skip_until_space(const char *s)
{
while (*s && *s != ' ') s++;
return s;
}
#endif
static const char *
skip_until_space2(const char *s, Py_ssize_t *len)
{
@ -81,9 +72,8 @@ typecast_parse_date(const char* s, const char** t, Py_ssize_t* len,
cz += 1;
}
/* Is this a BC date? If so, adjust the year value. Note that
* mx.DateTime numbers BC dates from zero rather than one. The
* Python datetime module does not support BC dates at all. */
/* Is this a BC date? If so, adjust the year value. Note that the Python
* datetime module does not support BC dates at all. */
if (*len >= 2 && s[*len-2] == 'B' && s[*len-1] == 'C')
*year = 1 - (*year);
@ -175,10 +165,6 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
#include "psycopg/typecast_binary.c"
#include "psycopg/typecast_datetime.c"
#ifdef HAVE_MXDATETIME
#include "psycopg/typecast_mxdatetime.c"
#endif
#include "psycopg/typecast_array.c"
static long int typecast_default_DEFAULT[] = {0};
@ -217,30 +203,6 @@ static typecastObject_initlist typecast_pydatetime[] = {
{NULL, NULL, NULL}
};
#ifdef HAVE_MXDATETIME
#define typecast_MXDATETIMEARRAY_cast typecast_GENERIC_ARRAY_cast
#define typecast_MXDATETIMETZARRAY_cast typecast_GENERIC_ARRAY_cast
#define typecast_MXDATEARRAY_cast typecast_GENERIC_ARRAY_cast
#define typecast_MXTIMEARRAY_cast typecast_GENERIC_ARRAY_cast
#define typecast_MXINTERVALARRAY_cast typecast_GENERIC_ARRAY_cast
/* a list of initializers, used to make the typecasters accessible anyway */
static typecastObject_initlist typecast_mxdatetime[] = {
{"MXDATETIME", typecast_DATETIME_types, typecast_MXDATE_cast},
{"MXDATETIMETZ", typecast_DATETIMETZ_types, typecast_MXDATE_cast},
{"MXTIME", typecast_TIME_types, typecast_MXTIME_cast},
{"MXDATE", typecast_DATE_types, typecast_MXDATE_cast},
{"MXINTERVAL", typecast_INTERVAL_types, typecast_MXINTERVAL_cast},
{"MXDATETIMEARRAY", typecast_DATETIMEARRAY_types, typecast_MXDATETIMEARRAY_cast, "MXDATETIME"},
{"MXDATETIMETZARRAY", typecast_DATETIMETZARRAY_types, typecast_MXDATETIMETZARRAY_cast, "MXDATETIMETZ"},
{"MXTIMEARRAY", typecast_TIMEARRAY_types, typecast_MXTIMEARRAY_cast, "MXTIME"},
{"MXDATEARRAY", typecast_DATEARRAY_types, typecast_MXDATEARRAY_cast, "MXDATE"},
{"MXINTERVALARRAY", typecast_INTERVALARRAY_types, typecast_MXINTERVALARRAY_cast, "MXINTERVAL"},
{NULL, NULL, NULL}
};
#endif
/** the type dictionary and associated functions **/
PyObject *psyco_types;
@ -292,19 +254,6 @@ typecast_init(PyObject *module)
psyco_default_cast = typecast_from_c(&typecast_default, dict);
/* register the date/time typecasters with their original names */
#ifdef HAVE_MXDATETIME
if (0 == psyco_typecast_mxdatetime_init()) {
for (i = 0; typecast_mxdatetime[i].name != NULL; i++) {
Dprintf("typecast_init: initializing %s", typecast_mxdatetime[i].name);
t = (typecastObject *)typecast_from_c(&(typecast_mxdatetime[i]), dict);
if (t == NULL) { goto exit; }
PyDict_SetItem(dict, t->name, (PyObject *)t);
Py_DECREF((PyObject *)t);
t = NULL;
}
}
#endif
if (0 > psyco_typecast_datetime_init()) { goto exit; }
for (i = 0; typecast_pydatetime[i].name != NULL; i++) {
Dprintf("typecast_init: initializing %s", typecast_pydatetime[i].name);

View File

@ -458,10 +458,8 @@ typecast_PYINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
/* psycopg defaults to using python datetime types */
#ifdef PSYCOPG_DEFAULT_PYDATETIME
#define typecast_DATE_cast typecast_PYDATE_cast
#define typecast_TIME_cast typecast_PYTIME_cast
#define typecast_INTERVAL_cast typecast_PYINTERVAL_cast
#define typecast_DATETIME_cast typecast_PYDATETIME_cast
#define typecast_DATETIMETZ_cast typecast_PYDATETIMETZ_cast
#endif

View File

@ -1,252 +0,0 @@
/* typecast_mxdatetime.c - date and time typecasting functions to mx types
*
* Copyright (C) 2001-2019 Federico Di Gregorio <fog@debian.org>
*
* This file is part of psycopg.
*
* psycopg2 is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, as a special exception, the copyright holders give
* permission to link this program with the OpenSSL library (or with
* modified versions of OpenSSL that use the same license as OpenSSL),
* and distribute linked combinations including the two.
*
* You must obey the GNU Lesser General Public License in all respects for
* all of the code used other than OpenSSL.
*
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*/
#include "mxDateTime.h"
/* Return 0 on success, -1 on failure, but don't set an exception */
static int
psyco_typecast_mxdatetime_init(void)
{
Dprintf("psyco_typecast_mxdatetime_init: mx.DateTime init");
if (mxDateTime_ImportModuleAndAPI()) {
Dprintf("psyco_typecast_mxdatetime_init: mx.DateTime initialization failed");
PyErr_Clear();
return -1;
}
return 0;
}
/** DATE - cast a date into mx.DateTime python object **/
static PyObject *
typecast_MXDATE_cast(const char *str, Py_ssize_t len, PyObject *curs)
{
int n, y=0, m=0, d=0;
int hh=0, mm=0, ss=0, us=0, tz=0;
const char *tp = NULL;
if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXDATE_cast: s = %s", str);
/* check for infinity */
if (!strcmp(str, "infinity") || !strcmp(str, "-infinity")) {
if (str[0] == '-') {
return mxDateTime.DateTime_FromDateAndTime(-999998,1,1, 0,0,0);
}
else {
return mxDateTime.DateTime_FromDateAndTime(999999,12,31, 0,0,0);
}
}
n = typecast_parse_date(str, &tp, &len, &y, &m, &d);
Dprintf("typecast_MXDATE_cast: tp = %p n = %d,"
" len = " FORMAT_CODE_PY_SSIZE_T ","
" 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) {
n = typecast_parse_time(tp, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_MXDATE_cast: n = %d,"
" 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 != 0 && (n < 3 || n > 6)) {
PyErr_SetString(DataError, "unable to parse time");
return NULL;
}
}
Dprintf("typecast_MXDATE_cast: fractionary seconds: %lf",
(double)ss + (double)us/(double)1000000.0);
return mxDateTime.DateTime_FromDateAndTime(y, m, d, hh, mm,
(double)ss + (double)us/(double)1000000.0);
}
/** TIME - parse time into an mx.DateTime object **/
static PyObject *
typecast_MXTIME_cast(const char *str, Py_ssize_t len, PyObject *curs)
{
int n, hh=0, mm=0, ss=0, us=0, tz=0;
if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXTIME_cast: s = %s", str);
n = typecast_parse_time(str, NULL, &len, &hh, &mm, &ss, &us, &tz);
Dprintf("typecast_MXTIME_cast: time parsed, %d components", n);
Dprintf("typecast_MXTIME_cast: hh = %d, mm = %d, ss = %d, us = %d",
hh, mm, ss, us);
if (n < 3 || n > 6) {
PyErr_SetString(DataError, "unable to parse time");
return NULL;
}
Dprintf("typecast_MXTIME_cast: fractionary seconds: %lf",
(double)ss + (double)us/(double)1000000.0);
return mxDateTime.DateTimeDelta_FromTime(hh, mm,
(double)ss + (double)us/(double)1000000.0);
}
/** INTERVAL - parse an interval into an mx.DateTimeDelta **/
static PyObject *
typecast_MXINTERVAL_cast(const char *str, Py_ssize_t len, PyObject *curs)
{
long years = 0, months = 0, days = 0, denominator = 1;
double hours = 0.0, minutes = 0.0, seconds = 0.0, hundredths = 0.0;
double v = 0.0, sign = 1.0;
int part = 0;
if (str == NULL) { Py_RETURN_NONE; }
Dprintf("typecast_MXINTERVAL_cast: s = %s", str);
while (*str) {
switch (*str) {
case '-':
sign = -1.0;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
v = v * 10.0 + (double)(*str - '0');
Dprintf("typecast_MXINTERVAL_cast: v = %f", v);
if (part == 6){
denominator *= 10;
Dprintf("typecast_MXINTERVAL_cast: denominator = %ld",
denominator);
}
break;
case 'y':
if (part == 0) {
years = (long)(v*sign);
str = skip_until_space(str);
Dprintf("typecast_MXINTERVAL_cast: years = %ld, rest = %s",
years, str);
v = 0.0; sign = 1.0; part = 1;
}
break;
case 'm':
if (part <= 1) {
months = (long)(v*sign);
str = skip_until_space(str);
Dprintf("typecast_MXINTERVAL_cast: months = %ld, rest = %s",
months, str);
v = 0.0; sign = 1.0; part = 2;
}
break;
case 'd':
if (part <= 2) {
days = (long)(v*sign);
str = skip_until_space(str);
Dprintf("typecast_MXINTERVAL_cast: days = %ld, rest = %s",
days, str);
v = 0.0; sign = 1.0; part = 3;
}
break;
case ':':
if (part <= 3) {
hours = v;
Dprintf("typecast_MXINTERVAL_cast: hours = %f", hours);
v = 0.0; part = 4;
}
else if (part == 4) {
minutes = v;
Dprintf("typecast_MXINTERVAL_cast: minutes = %f", minutes);
v = 0.0; part = 5;
}
break;
case '.':
if (part == 5) {
seconds = v;
Dprintf("typecast_MXINTERVAL_cast: seconds = %f", seconds);
v = 0.0; part = 6;
}
break;
default:
break;
}
str++;
}
/* manage last value, be it minutes or seconds or hundredths of a second */
if (part == 4) {
minutes = v;
Dprintf("typecast_MXINTERVAL_cast: minutes = %f", minutes);
}
else if (part == 5) {
seconds = v;
Dprintf("typecast_MXINTERVAL_cast: seconds = %f", seconds);
}
else if (part == 6) {
hundredths = v;
Dprintf("typecast_MXINTERVAL_cast: hundredths = %f", hundredths);
hundredths = hundredths/denominator;
Dprintf("typecast_MXINTERVAL_cast: fractions = %.20f", hundredths);
}
/* calculates seconds */
if (sign < 0.0) {
seconds = - (hundredths + seconds + minutes*60 + hours*3600);
}
else {
seconds += hundredths + minutes*60 + hours*3600;
}
/* calculates days */
days += years*365 + months*30;
Dprintf("typecast_MXINTERVAL_cast: days = %ld, seconds = %f",
days, seconds);
return mxDateTime.DateTimeDelta_FromDaysAndSeconds(days, seconds);
}
/* psycopg defaults to using mx types */
#ifdef PSYCOPG_DEFAULT_MXDATETIME
#define typecast_DATE_cast typecast_MXDATE_cast
#define typecast_TIME_cast typecast_MXTIME_cast
#define typecast_INTERVAL_cast typecast_MXINTERVAL_cast
#define typecast_DATETIME_cast typecast_MXDATE_cast
#define typecast_DATETIMETZ_cast typecast_MXDATE_cast
#endif

View File

@ -58,7 +58,6 @@
<None Include="psycopg\adapter_binary.h" />
<None Include="psycopg\adapter_datetime.h" />
<None Include="psycopg\adapter_list.h" />
<None Include="psycopg\adapter_mxdatetime.h" />
<None Include="psycopg\adapter_pboolean.h" />
<None Include="psycopg\adapter_qstring.h" />
<None Include="psycopg\config.h" />
@ -166,7 +165,6 @@
<Compile Include="psycopg\adapter_binary.c" />
<Compile Include="psycopg\adapter_datetime.c" />
<Compile Include="psycopg\adapter_list.c" />
<Compile Include="psycopg\adapter_mxdatetime.c" />
<Compile Include="psycopg\adapter_pboolean.c" />
<Compile Include="psycopg\adapter_qstring.c" />
<Compile Include="psycopg\connection_int.c" />
@ -187,7 +185,6 @@
<Compile Include="psycopg\typecast_binary.c" />
<Compile Include="psycopg\typecast_builtins.c" />
<Compile Include="psycopg\typecast_datetime.c" />
<Compile Include="psycopg\typecast_mxdatetime.c" />
<Compile Include="psycopg\utils.c" />
<Compile Include="psycopg\win32_support.c" />
<Compile Include="psycopg\lobject_int.c" />

View File

@ -7,13 +7,6 @@ define=
# different name set the following option to the pg_config full path.
pg_config=
# Set to 1 to use Python datetime objects for default date/time representation.
use_pydatetime=1
# If the build system does not find the mx.DateTime headers, try
# setting its value to the right path.
mx_include_dir=
# For Windows only:
# Set to 1 if the PostgreSQL library was built with OpenSSL.
# Required to link in OpenSSL libraries and dependencies.

View File

@ -241,7 +241,6 @@ class psycopg_build_ext(build_ext):
def initialize_options(self):
build_ext.initialize_options(self)
self.pgdir = None
self.mx_include_dir = None
self.have_ssl = have_ssl
self.static_libpq = static_libpq
self.pg_config = None
@ -523,45 +522,6 @@ depends = [
parser = configparser.ConfigParser()
parser.read('setup.cfg')
# Choose a datetime module
have_pydatetime = True
have_mxdatetime = False
use_pydatetime = parser.getboolean('build_ext', 'use_pydatetime')
# check for mx package
mxincludedir = ''
if parser.has_option('build_ext', 'mx_include_dir'):
mxincludedir = parser.get('build_ext', 'mx_include_dir')
if not mxincludedir:
mxincludedir = os.path.join(get_python_inc(plat_specific=1), "mx")
if mxincludedir.strip() and os.path.exists(mxincludedir):
# Build the support for mx: we will check at runtime if it can be imported
include_dirs.append(mxincludedir)
define_macros.append(('HAVE_MXDATETIME', '1'))
sources.append('adapter_mxdatetime.c')
depends.extend(['adapter_mxdatetime.h', 'typecast_mxdatetime.c'])
have_mxdatetime = True
version_flags.append('mx')
# now decide which package will be the default for date/time typecasts
if have_pydatetime and (use_pydatetime or not have_mxdatetime):
define_macros.append(('PSYCOPG_DEFAULT_PYDATETIME', '1'))
elif have_mxdatetime:
define_macros.append(('PSYCOPG_DEFAULT_MXDATETIME', '1'))
else:
error_message = """\
psycopg requires a datetime module:
mx.DateTime module not found
python datetime module not found
Note that psycopg needs the module headers and not just the module
itself. If you installed Python or mx.DateTime from a binary package
you probably need to install its companion -dev or -devel package."""
for line in error_message.split("\n"):
sys.stderr.write("error: " + line)
sys.exit(1)
# generate a nice version string to avoid confusion when users report bugs
version_flags.append('pq3') # no more a choice
version_flags.append('ext') # no more a choice

View File

@ -447,184 +447,6 @@ class DatetimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
self.assertRaises(psycopg2.NotSupportedError, cur.fetchone)
@unittest.skipUnless(
hasattr(psycopg2._psycopg, 'MXDATETIME'),
'Requires mx.DateTime support'
)
class mxDateTimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
"""Tests for the mx.DateTime based date handling in psycopg2."""
def setUp(self):
ConnectingTestCase.setUp(self)
self.curs = self.conn.cursor()
self.DATE = psycopg2._psycopg.MXDATE
self.TIME = psycopg2._psycopg.MXTIME
self.DATETIME = psycopg2._psycopg.MXDATETIME
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):
self.conn.close()
def test_parse_bc_date(self):
value = self.DATE('00042-01-01 BC', self.curs)
self.assert_(value is not 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', self.curs)
self.assert_(value is not 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', self.curs)
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', self.curs)
self.assertEqual(math.floor(value.second), 29)
self.assertEqual(
int((value.second - math.floor(value.second)) * 1000000), 123456)
def test_parse_time_timezone(self):
# Time zone information is ignored.
from mx.DateTime import Time
expected = Time(13, 30, 29)
self.assertEqual(expected, self.TIME("13:30:29+01", self.curs))
self.assertEqual(expected, self.TIME("13:30:29-01", self.curs))
self.assertEqual(expected, self.TIME("13:30:29+01:15", self.curs))
self.assertEqual(expected, self.TIME("13:30:29-01:15", self.curs))
self.assertEqual(expected, self.TIME("13:30:29+01:15:42", self.curs))
self.assertEqual(expected, self.TIME("13:30:29-01:15:42", self.curs))
def test_parse_datetime_timezone(self):
# Time zone information is ignored.
from mx.DateTime import DateTime
expected = DateTime(2007, 1, 1, 13, 30, 29)
self.assertEqual(
expected, self.DATETIME("2007-01-01 13:30:29+01", self.curs))
self.assertEqual(
expected, self.DATETIME("2007-01-01 13:30:29-01", self.curs))
self.assertEqual(
expected, self.DATETIME("2007-01-01 13:30:29+01:15", self.curs))
self.assertEqual(
expected, self.DATETIME("2007-01-01 13:30:29-01:15", self.curs))
self.assertEqual(
expected, self.DATETIME("2007-01-01 13:30:29+01:15:42", self.curs))
self.assertEqual(
expected, self.DATETIME("2007-01-01 13:30:29-01:15:42", self.curs))
def test_parse_interval(self):
value = self.INTERVAL('42 days 05:50:05', self.curs)
self.assert_(value is not 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)])
# microsecs for BC timestamps look not available in PG < 8.4
# but more likely it's determined at compile time.
self.assert_(value in (
'0042-01-01 13:30:29.123456 BC',
'0042-01-01 13:30:29 BC'), value)
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_negative_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)
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, 5, 3))
def test_type_roundtrip_datetime(self):
from mx.DateTime import DateTime
self._test_type_roundtrip(DateTime(2010, 5, 3, 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, 5, 3))
def test_type_roundtrip_datetime_array(self):
from mx.DateTime import DateTime
self._test_type_roundtrip_array(DateTime(2010, 5, 3, 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))
class FromTicksTestCase(unittest.TestCase):
# bug "TimestampFromTicks() throws ValueError (2-2.0.14)"
# reported by Jozsef Szalay on 2010-05-06