Drop support for mx.DateTime objects

They are Python 2 only.
This commit is contained in:
Daniele Varrazzo 2021-06-14 23:05:48 +01:00
parent 476a969bd8
commit 2eac70786e
15 changed files with 7 additions and 967 deletions

1
NEWS
View File

@ -19,6 +19,7 @@ What's new in psycopg 2.9
Other changes: Other changes:
- Dropped support for Python 2.7, 3.4, 3.5 (:tickets:`#1198, #1000, #1197`). - Dropped support for Python 2.7, 3.4, 3.5 (:tickets:`#1198, #1000, #1197`).
- Dropped support for mx.DateTime.
- Build system for Linux/MacOS binary packages moved to GitHub action, now - Build system for Linux/MacOS binary packages moved to GitHub action, now
providing :pep:`600`\-style wheels packages. providing :pep:`600`\-style wheels packages.

View File

@ -128,8 +128,6 @@ rst_epilog = """
.. _transaction isolation level: .. _transaction isolation level:
https://www.postgresql.org/docs/current/static/transaction-iso.html 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)` .. |MVCC| replace:: :abbr:`MVCC (Multiversion concurrency control)`
""" """

View File

@ -453,13 +453,6 @@ deal with Python objects adaptation:
Specialized adapters for Python datetime objects. Specialized adapters for Python datetime objects.
.. class:: DateFromMx
TimeFromMx
TimestampFromMx
IntervalFromMx
Specialized adapters for `mx.DateTime`_ objects.
.. data:: adapters .. data:: adapters
Dictionary of the currently registered object adapters. Use Dictionary of the currently registered object adapters. Use
@ -1004,20 +997,6 @@ from the database. See :ref:`unicode-handling` for details.
Typecasters to convert time-related data types to Python `!datetime` Typecasters to convert time-related data types to Python `!datetime`
objects. 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 .. versionchanged:: 2.2
previously the `DECIMAL` typecaster and the specific time-related previously the `DECIMAL` typecaster and the specific time-related
typecasters (`!PY*` and `!MX*`) were not exposed by the `extensions` typecasters (`!PY*` and `!MX*`) were not exposed by the `extensions`

View File

@ -230,7 +230,6 @@ If you have less standard requirements such as:
- creating a :ref:`debug build <debug-build>`, - creating a :ref:`debug build <debug-build>`,
- using :program:`pg_config` not in the :envvar:`PATH`, - using :program:`pg_config` not in the :envvar:`PATH`,
- supporting ``mx.DateTime``,
then take a look at the ``setup.cfg`` file. 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: Date objects; Adaptation
single: Time objects; Adaptation single: Time objects; Adaptation
single: Interval objects; Adaptation single: Interval objects; Adaptation
single: mx.DateTime; Adaptation
.. _adapt-date: .. _adapt-date:
@ -550,8 +549,7 @@ Date/Time objects adaptation
Python builtin `~datetime.datetime`, `~datetime.date`, Python builtin `~datetime.datetime`, `~datetime.date`,
`~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's `~datetime.time`, `~datetime.timedelta` are converted into PostgreSQL's
:sql:`timestamp[tz]`, :sql:`date`, :sql:`time[tz]`, :sql:`interval` data types. :sql:`timestamp[tz]`, :sql:`date`, :sql:`time[tz]`, :sql:`interval` data types.
Time zones are supported too. The Egenix `mx.DateTime`_ objects are adapted Time zones are supported too.
the same way::
>>> dt = datetime.datetime.now() >>> dt = datetime.datetime.now()
>>> dt >>> dt

View File

@ -42,14 +42,6 @@ from psycopg2._psycopg import ( # noqa
ROWIDARRAY, STRINGARRAY, TIME, TIMEARRAY, UNICODE, UNICODEARRAY, ROWIDARRAY, STRINGARRAY, TIME, TIMEARRAY, UNICODE, UNICODEARRAY,
AsIs, Binary, Boolean, Float, Int, QuotedString, ) 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
from psycopg2._psycopg import ( # noqa from psycopg2._psycopg import ( # noqa
PYDATE, PYDATETIME, PYDATETIMETZ, PYINTERVAL, PYTIME, PYDATEARRAY, PYDATE, PYDATETIME, PYDATETIMETZ, PYINTERVAL, PYTIME, PYDATEARRAY,
PYDATETIMEARRAY, PYDATETIMETZARRAY, PYINTERVALARRAY, PYTIMEARRAY, PYDATETIMEARRAY, PYDATETIMETZARRAY, PYINTERVALARRAY, PYTIMEARRAY,

View File

@ -1,302 +0,0 @@
/* adapter_mxdatetime.c - mx date/time objects
*
* Copyright (C) 2003-2019 Federico Di Gregorio <fog@debian.org>
* Copyright (C) 2020 The Psycopg Team
*
* 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)
{
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 **/
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,70 +0,0 @@
/* adapter_mxdatetime.h - definition for the mx date/time types
*
* Copyright (C) 2003-2019 Federico Di Gregorio <fog@debian.org>
* Copyright (C) 2020 The Psycopg Team
*
* 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;
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

@ -53,11 +53,6 @@
#include "psycopg/adapter_list.h" #include "psycopg/adapter_list.h"
#include "psycopg/typecast_binary.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 */ /* some module-level variables, like the datetime module */
#include <datetime.h> #include <datetime.h>
#include "psycopg/adapter_datetime.h" #include "psycopg/adapter_datetime.h"
@ -358,30 +353,6 @@ adapters_init(PyObject *module)
if (0 > microprotocols_add(PyDateTimeAPI->DeltaType, NULL, obj)) { goto exit; } if (0 > microprotocols_add(PyDateTimeAPI->DeltaType, NULL, obj)) { goto exit; }
Py_CLEAR(obj); 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! */ /* Success! */
rv = 0; rv = 0;
@ -943,34 +914,6 @@ datetime_init(void)
return 0; return 0;
} }
RAISES_NEG static int
mxdatetime_init(PyObject *module)
{
Dprintf("psycopgmodule: initializing mx.DateTime module");
#ifdef HAVE_MXDATETIME
Py_SET_TYPE(&mxdatetimeType, &PyType_Type);
if (0 > PyType_Ready(&mxdatetimeType)) { return -1; }
if (mxDateTime_ImportModuleAndAPI()) {
Dprintf("psycopgmodule: mx.DateTime module import failed");
PyErr_Clear();
}
/* 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 **/ /** method table and module initialization **/
static PyMethodDef psycopgMethods[] = { static PyMethodDef psycopgMethods[] = {
@ -1014,18 +957,6 @@ static PyMethodDef psycopgMethods[] = {
{"IntervalFromPy", (PyCFunction)psyco_IntervalFromPy, {"IntervalFromPy", (PyCFunction)psyco_IntervalFromPy,
METH_VARARGS, psyco_IntervalFromPy_doc}, 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, {"set_wait_callback", (PyCFunction)psyco_set_wait_callback,
METH_O, psyco_set_wait_callback_doc}, METH_O, psyco_set_wait_callback_doc},
{"get_wait_callback", (PyCFunction)psyco_get_wait_callback, {"get_wait_callback", (PyCFunction)psyco_get_wait_callback,
@ -1086,7 +1017,6 @@ INIT_MODULE(_psycopg)(void)
if (0 > add_module_constants(module)) { goto exit; } if (0 > add_module_constants(module)) { goto exit; }
if (0 > add_module_types(module)) { goto exit; } if (0 > add_module_types(module)) { goto exit; }
if (0 > datetime_init()) { goto exit; } if (0 > datetime_init()) { goto exit; }
if (0 > mxdatetime_init(module)) { goto exit; }
if (0 > encodings_init(module)) { goto exit; } if (0 > encodings_init(module)) { goto exit; }
if (0 > typecast_init(module)) { goto exit; } if (0 > typecast_init(module)) { goto exit; }
if (0 > adapters_init(module)) { goto exit; } if (0 > adapters_init(module)) { goto exit; }

View File

@ -32,15 +32,6 @@
/* useful function used by some typecasters */ /* 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 * static const char *
skip_until_space2(const char *s, Py_ssize_t *len) skip_until_space2(const char *s, Py_ssize_t *len)
{ {
@ -82,11 +73,11 @@ typecast_parse_date(const char* s, const char** t, Py_ssize_t* len,
cz += 1; cz += 1;
} }
/* Is this a BC date? If so, adjust the year value. Note that /* Is this a BC date? If so, adjust the year value. However
* mx.DateTime numbers BC dates from zero rather than one. The * Python datetime module does not support BC dates, so this will raise
* Python datetime module does not support BC dates at all. */ * an exception downstream. */
if (*len >= 2 && s[*len-2] == 'B' && s[*len-1] == 'C') if (*len >= 2 && s[*len-2] == 'B' && s[*len-1] == 'C')
*year = 1 - (*year); *year = -(*year);
if (t != NULL) *t = s; if (t != NULL) *t = s;
@ -175,11 +166,6 @@ typecast_parse_time(const char* s, const char** t, Py_ssize_t* len,
#include "psycopg/typecast_basic.c" #include "psycopg/typecast_basic.c"
#include "psycopg/typecast_binary.c" #include "psycopg/typecast_binary.c"
#include "psycopg/typecast_datetime.c" #include "psycopg/typecast_datetime.c"
#ifdef HAVE_MXDATETIME
#include "psycopg/typecast_mxdatetime.c"
#endif
#include "psycopg/typecast_array.c" #include "psycopg/typecast_array.c"
static long int typecast_default_DEFAULT[] = {0}; static long int typecast_default_DEFAULT[] = {0};
@ -218,29 +204,6 @@ static typecastObject_initlist typecast_pydatetime[] = {
{NULL, NULL, NULL} {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 **/ /** the type dictionary and associated functions **/
@ -291,18 +254,6 @@ typecast_init(PyObject *module)
psyco_default_cast = typecast_from_c(&typecast_default, dict); psyco_default_cast = typecast_from_c(&typecast_default, dict);
/* register the date/time typecasters with their original names */ /* register the date/time typecasters with their original names */
#ifdef HAVE_MXDATETIME
if (0 == typecast_mxdatetime_init()) {
for (i = 0; typecast_mxdatetime[i].name != NULL; i++) {
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 > typecast_datetime_init()) { goto exit; } if (0 > typecast_datetime_init()) { goto exit; }
for (i = 0; typecast_pydatetime[i].name != NULL; i++) { for (i = 0; typecast_pydatetime[i].name != NULL; i++) {
t = (typecastObject *)typecast_from_c(&(typecast_pydatetime[i]), dict); t = (typecastObject *)typecast_from_c(&(typecast_pydatetime[i]), dict);

View File

@ -1,241 +0,0 @@
/* typecast_mxdatetime.c - date and time typecasting functions to mx types
*
* Copyright (C) 2001-2019 Federico Di Gregorio <fog@debian.org>
* Copyright (C) 2020 The Psycopg Team
*
* 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
typecast_mxdatetime_init(void)
{
if (mxDateTime_ImportModuleAndAPI()) {
Dprintf("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);
}

View File

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

View File

@ -1,16 +1,12 @@
[build_ext] [build_ext]
# PSYCOPG_DEBUG can be added to enable verbose debug information # PSYCOPG_DEBUG can be added to enable verbose debug information
define= define=PSYCOPG_DEBUG
# "pg_config" is required to locate PostgreSQL headers and libraries needed to # "pg_config" is required to locate PostgreSQL headers and libraries needed to
# build psycopg2. If pg_config is not in the path or is installed under a # build psycopg2. If pg_config is not in the path or is installed under a
# different name set the following option to the pg_config full path. # different name set the following option to the pg_config full path.
pg_config= pg_config=
# 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: # For Windows only:
# Set to 1 if the PostgreSQL library was built with OpenSSL. # Set to 1 if the PostgreSQL library was built with OpenSSL.
# Required to link in OpenSSL libraries and dependencies. # Required to link in OpenSSL libraries and dependencies.

View File

@ -230,7 +230,6 @@ class psycopg_build_ext(build_ext):
def initialize_options(self): def initialize_options(self):
build_ext.initialize_options(self) build_ext.initialize_options(self)
self.pgdir = None self.pgdir = None
self.mx_include_dir = None
self.have_ssl = have_ssl self.have_ssl = have_ssl
self.static_libpq = static_libpq self.static_libpq = static_libpq
self.pg_config = None self.pg_config = None
@ -497,24 +496,6 @@ depends = [
parser = configparser.ConfigParser() parser = configparser.ConfigParser()
parser.read('setup.cfg') parser.read('setup.cfg')
# 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:
# look for mxDateTime.h; prefer one located in venv
candidate_dirs = [os.path.join(d, 'mx', 'DateTime', 'mxDateTime') for d in sys.path] \
+ [os.path.join(get_python_inc(plat_specific=1), "mx")]
candidate_dirs = [d for d in candidate_dirs if os.path.exists(os.path.join(d, 'mxDateTime.h'))] or ['']
mxincludedir = candidate_dirs[0]
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'])
version_flags.append('mx')
# generate a nice version string to avoid confusion when users report bugs # generate a nice version string to avoid confusion when users report bugs
version_flags.append('pq3') # no more a choice version_flags.append('pq3') # no more a choice
version_flags.append('ext') # no more a choice version_flags.append('ext') # no more a choice

View File

@ -33,12 +33,6 @@ from psycopg2.tz import FixedOffsetTimezone, ZERO
import unittest import unittest
from .testutils import ConnectingTestCase, skip_before_postgres, skip_if_crdb from .testutils import ConnectingTestCase, skip_before_postgres, skip_if_crdb
try:
from mx.DateTime import Date, Time, DateTime, DateTimeDeltaFrom
except ImportError:
# Tests will be skipped
pass
def total_seconds(d): def total_seconds(d):
"""Return total number of seconds of a timedelta as a float.""" """Return total number of seconds of a timedelta as a float."""
@ -456,169 +450,6 @@ class DatetimeTests(ConnectingTestCase, CommonDatetimeTestsMixin):
self.assertRaises(psycopg2.NotSupportedError, cur.fetchone) 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.
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.
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):
value = self.execute('select (%s)::time::text',
[Time(13, 30, 29)])
self.assertEqual(value, '13:30:29')
def test_adapt_datetime(self):
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):
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):
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):
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):
self._test_type_roundtrip(Date(2010, 5, 3))
def test_type_roundtrip_datetime(self):
self._test_type_roundtrip(DateTime(2010, 5, 3, 10, 20, 30))
def test_type_roundtrip_time(self):
self._test_type_roundtrip(Time(10, 20, 30))
def test_type_roundtrip_interval(self):
self._test_type_roundtrip(DateTimeDeltaFrom(seconds=30))
def test_type_roundtrip_date_array(self):
self._test_type_roundtrip_array(Date(2010, 5, 3))
def test_type_roundtrip_datetime_array(self):
self._test_type_roundtrip_array(DateTime(2010, 5, 3, 10, 20, 30))
def test_type_roundtrip_time_array(self):
self._test_type_roundtrip_array(Time(10, 20, 30))
def test_type_roundtrip_interval_array(self):
self._test_type_roundtrip_array(DateTimeDeltaFrom(seconds=30))
class FromTicksTestCase(unittest.TestCase): class FromTicksTestCase(unittest.TestCase):
# bug "TimestampFromTicks() throws ValueError (2-2.0.14)" # bug "TimestampFromTicks() throws ValueError (2-2.0.14)"
# reported by Jozsef Szalay on 2010-05-06 # reported by Jozsef Szalay on 2010-05-06