psycopg2/psycopg/psycopgmodule.c
Daniele Varrazzo e9c476266c Decrement the refcount of temporary objects in module init failed
We are going to die anyway, but let's do it in style.
2019-01-22 19:40:42 +00:00

1023 lines
31 KiB
C

/* psycopgmodule.c - psycopg module (will import other C classes)
*
* Copyright (C) 2003-2010 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/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/replication_connection.h"
#include "psycopg/replication_cursor.h"
#include "psycopg/replication_message.h"
#include "psycopg/green.h"
#include "psycopg/column.h"
#include "psycopg/lobject.h"
#include "psycopg/notify.h"
#include "psycopg/xid.h"
#include "psycopg/typecast.h"
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/conninfo.h"
#include "psycopg/diagnostics.h"
#include "psycopg/adapter_qstring.h"
#include "psycopg/adapter_binary.h"
#include "psycopg/adapter_pboolean.h"
#include "psycopg/adapter_pint.h"
#include "psycopg/adapter_pfloat.h"
#include "psycopg/adapter_pdecimal.h"
#include "psycopg/adapter_asis.h"
#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"
HIDDEN PyObject *psycoEncodings = NULL;
#ifdef PSYCOPG_DEBUG
HIDDEN int psycopg_debug_enabled = 0;
#endif
/* Python representation of SQL NULL */
HIDDEN PyObject *psyco_null = NULL;
/* macro trick to stringify a macro expansion */
#define xstr(s) str(s)
#define str(s) #s
/** connect module-level function **/
#define psyco_connect_doc \
"_connect(dsn, [connection_factory], [async]) -- New database connection.\n\n"
static PyObject *
psyco_connect(PyObject *self, PyObject *args, PyObject *keywds)
{
PyObject *conn = NULL;
PyObject *factory = NULL;
const char *dsn = NULL;
int async = 0, async_ = 0;
static char *kwlist[] = {"dsn", "connection_factory", "async", "async_", NULL};
if (!PyArg_ParseTupleAndKeywords(args, keywds, "s|Oii", kwlist,
&dsn, &factory, &async, &async_)) {
return NULL;
}
if (async_) { async = async_; }
Dprintf("psyco_connect: dsn = '%s', async = %d", dsn, async);
/* allocate connection, fill with errors and return it */
if (factory == NULL || factory == Py_None) {
factory = (PyObject *)&connectionType;
}
/* Here we are breaking the connection.__init__ interface defined
* by psycopg2. So, if not requiring an async conn, avoid passing
* the async parameter. */
/* TODO: would it be possible to avoid an additional parameter
* to the conn constructor? A subclass? (but it would require mixins
* to further subclass) Another dsn parameter (but is not really
* a connection parameter that can be configured) */
if (!async) {
conn = PyObject_CallFunction(factory, "s", dsn);
} else {
conn = PyObject_CallFunction(factory, "si", dsn, async);
}
return conn;
}
#define psyco_parse_dsn_doc \
"parse_dsn(dsn) -> dict -- parse a connection string into parameters"
static PyObject *
psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
{
char *err = NULL;
PQconninfoOption *options = NULL;
PyObject *res = NULL, *dsn;
static char *kwlist[] = {"dsn", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) {
return NULL;
}
Py_INCREF(dsn); /* for ensure_bytes */
if (!(dsn = psycopg_ensure_bytes(dsn))) { goto exit; }
options = PQconninfoParse(Bytes_AS_STRING(dsn), &err);
if (options == NULL) {
if (err != NULL) {
PyErr_Format(ProgrammingError, "invalid dsn: %s", err);
PQfreemem(err);
} else {
PyErr_SetString(OperationalError, "PQconninfoParse() failed");
}
goto exit;
}
res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 1);
exit:
PQconninfoFree(options); /* safe on null */
Py_XDECREF(dsn);
return res;
}
#define psyco_quote_ident_doc \
"quote_ident(str, conn_or_curs) -> str -- wrapper around PQescapeIdentifier\n\n" \
":Parameters:\n" \
" * `str`: A bytes or unicode object\n" \
" * `conn_or_curs`: A connection or cursor, required"
static PyObject *
psyco_quote_ident(PyObject *self, PyObject *args, PyObject *kwargs)
{
PyObject *ident = NULL, *obj = NULL, *result = NULL;
connectionObject *conn;
char *quoted = NULL;
static char *kwlist[] = {"ident", "scope", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO", kwlist, &ident, &obj)) {
return NULL;
}
if (PyObject_TypeCheck(obj, &cursorType)) {
conn = ((cursorObject*)obj)->conn;
}
else if (PyObject_TypeCheck(obj, &connectionType)) {
conn = (connectionObject*)obj;
}
else {
PyErr_SetString(PyExc_TypeError,
"argument 2 must be a connection or a cursor");
return NULL;
}
Py_INCREF(ident); /* for ensure_bytes */
if (!(ident = psycopg_ensure_bytes(ident))) { goto exit; }
if (!(quoted = psycopg_escape_identifier(conn,
Bytes_AS_STRING(ident), Bytes_GET_SIZE(ident)))) { goto exit; }
result = conn_text_from_chars(conn, quoted);
exit:
PQfreemem(quoted);
Py_XDECREF(ident);
return result;
}
/** type registration **/
#define psyco_register_type_doc \
"register_type(obj, conn_or_curs) -> None -- register obj with psycopg type system\n\n" \
":Parameters:\n" \
" * `obj`: A type adapter created by `new_type()`\n" \
" * `conn_or_curs`: A connection, cursor or None"
#define typecast_from_python_doc \
"new_type(oids, name, castobj) -> new type object\n\n" \
"Create a new binding object. The object can be used with the\n" \
"`register_type()` function to bind PostgreSQL objects to python objects.\n\n" \
":Parameters:\n" \
" * `oids`: Tuple of ``oid`` of the PostgreSQL types to convert.\n" \
" * `name`: Name for the new type\n" \
" * `adapter`: Callable to perform type conversion.\n" \
" It must have the signature ``fun(value, cur)`` where ``value`` is\n" \
" the string representation returned by PostgreSQL (`!None` if ``NULL``)\n" \
" and ``cur`` is the cursor from which data are read."
#define typecast_array_from_python_doc \
"new_array_type(oids, name, baseobj) -> new type object\n\n" \
"Create a new binding object to parse an array.\n\n" \
"The object can be used with `register_type()`.\n\n" \
":Parameters:\n" \
" * `oids`: Tuple of ``oid`` of the PostgreSQL types to convert.\n" \
" * `name`: Name for the new type\n" \
" * `baseobj`: Adapter to perform type conversion of a single array item."
static PyObject *
psyco_register_type(PyObject *self, PyObject *args)
{
PyObject *type, *obj = NULL;
if (!PyArg_ParseTuple(args, "O!|O", &typecastType, &type, &obj)) {
return NULL;
}
if (obj != NULL && obj != Py_None) {
if (PyObject_TypeCheck(obj, &cursorType)) {
PyObject **dict = &(((cursorObject*)obj)->string_types);
if (*dict == NULL) {
if (!(*dict = PyDict_New())) { return NULL; }
}
if (0 > typecast_add(type, *dict, 0)) { return NULL; }
}
else if (PyObject_TypeCheck(obj, &connectionType)) {
if (0 > typecast_add(type, ((connectionObject*)obj)->string_types, 0)) {
return NULL;
}
}
else {
PyErr_SetString(PyExc_TypeError,
"argument 2 must be a connection, cursor or None");
return NULL;
}
}
else {
if (0 > typecast_add(type, NULL, 0)) { return NULL; }
}
Py_RETURN_NONE;
}
/* Make sure libcrypto thread callbacks are set up. */
static void
psyco_libcrypto_threads_init(void)
{
PyObject *m;
Dprintf("psycopgmodule: configuring libpq libcrypto callbacks ");
/* importing the ssl module sets up Python's libcrypto callbacks */
if ((m = PyImport_ImportModule("ssl"))) {
/* disable libcrypto setup in libpq, so it won't stomp on the callbacks
that have already been set up */
PQinitOpenSSL(1, 0);
Py_DECREF(m);
}
else {
/* might mean that Python has been compiled without OpenSSL support,
fall back to relying on libpq's libcrypto locking */
PyErr_Clear();
}
}
/* Initialize the default adapters map
*
* Return 0 on success, else -1 and set an exception.
*/
RAISES_NEG static int
psyco_adapters_init(PyObject *mod)
{
PyObject *call = NULL;
int rv = -1;
Dprintf("psycopgmodule: configuring adapters");
if (0 != microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType)) {
goto exit;
}
#if PY_MAJOR_VERSION < 3
if (0 != microprotocols_add(&PyInt_Type, NULL, (PyObject*)&pintType)) {
goto exit;
}
#endif
if (0 != microprotocols_add(&PyLong_Type, NULL, (PyObject*)&pintType)) {
goto exit;
}
if (0 != microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType)) {
goto exit;
}
/* strings */
#if PY_MAJOR_VERSION < 3
if (0 != microprotocols_add(&PyString_Type, NULL, (PyObject*)&qstringType)) {
goto exit;
}
#endif
if (0 != microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType)) {
goto exit;
}
/* binary */
#if PY_MAJOR_VERSION < 3
if (0 != microprotocols_add(&PyBuffer_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#else
if (0 != microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#endif
#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 6
if (0 != microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#endif
#if PY_MAJOR_VERSION >= 3 || PY_MINOR_VERSION >= 7
if (0 != microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType)) {
goto exit;
}
#endif
if (0 != microprotocols_add(&PyList_Type, NULL, (PyObject*)&listType)) {
goto exit;
}
/* the module has already been initialized, so we can obtain the callable
objects directly from its dictionary :) */
if (!(call = PyMapping_GetItemString(mod, "DateFromPy"))) { goto exit; }
if (0 != microprotocols_add(PyDateTimeAPI->DateType, NULL, call)) { goto exit; }
Py_CLEAR(call);
if (!(call = PyMapping_GetItemString(mod, "TimeFromPy"))) { goto exit; }
if (0 != microprotocols_add(PyDateTimeAPI->TimeType, NULL, call)) { goto exit; }
Py_CLEAR(call);
if (!(call = PyMapping_GetItemString(mod, "TimestampFromPy"))) { goto exit; }
if (0 != microprotocols_add(PyDateTimeAPI->DateTimeType, NULL, call)) { goto exit; }
Py_CLEAR(call);
if (!(call = PyMapping_GetItemString(mod, "IntervalFromPy"))) { goto exit; }
if (0 != microprotocols_add(PyDateTimeAPI->DeltaType, NULL, call)) { goto exit; }
Py_CLEAR(call);
#ifdef HAVE_MXDATETIME
/* as above, we use the callable objects from the psycopg module */
if (NULL != (call = PyMapping_GetItemString(mod, "TimestampFromMx"))) {
if (0 != microprotocols_add(mxDateTime.DateTime_Type, NULL, call)) { goto exit; }
Py_CLEAR(call);
/* if we found the above, we have this too. */
if (!(call = PyMapping_GetItemString(mod, "TimeFromMx"))) { goto exit; }
if (0 != microprotocols_add(mxDateTime.DateTimeDelta_Type, NULL, call)) { goto exit; }
Py_CLEAR(call);
}
else {
PyErr_Clear();
}
#endif
/* Success! */
rv = 0;
exit:
Py_XDECREF(call);
return rv;
}
#define psyco_libpq_version_doc "Query actual libpq version loaded."
static PyObject*
psyco_libpq_version(PyObject *self, PyObject *dummy)
{
return PyInt_FromLong(PQlibVersion());
}
/* encrypt_password - Prepare the encrypted password form */
#define psyco_encrypt_password_doc \
"encrypt_password(password, user, [scope], [algorithm]) -- Prepares the encrypted form of a PostgreSQL password.\n\n"
static PyObject *
psyco_encrypt_password(PyObject *self, PyObject *args, PyObject *kwargs)
{
char *encrypted = NULL;
PyObject *password = NULL, *user = NULL;
PyObject *scope = Py_None, *algorithm = Py_None;
PyObject *res = NULL;
connectionObject *conn = NULL;
static char *kwlist[] = {"password", "user", "scope", "algorithm", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OO|OO", kwlist,
&password, &user, &scope, &algorithm)) {
return NULL;
}
/* for ensure_bytes */
Py_INCREF(user);
Py_INCREF(password);
Py_INCREF(algorithm);
if (scope != Py_None) {
if (PyObject_TypeCheck(scope, &cursorType)) {
conn = ((cursorObject*)scope)->conn;
}
else if (PyObject_TypeCheck(scope, &connectionType)) {
conn = (connectionObject*)scope;
}
else {
PyErr_SetString(PyExc_TypeError,
"the scope must be a connection or a cursor");
goto exit;
}
}
if (!(user = psycopg_ensure_bytes(user))) { goto exit; }
if (!(password = psycopg_ensure_bytes(password))) { goto exit; }
if (algorithm != Py_None) {
if (!(algorithm = psycopg_ensure_bytes(algorithm))) {
goto exit;
}
}
/* If we have to encrypt md5 we can use the libpq < 10 API */
if (algorithm != Py_None &&
strcmp(Bytes_AS_STRING(algorithm), "md5") == 0) {
encrypted = PQencryptPassword(
Bytes_AS_STRING(password), Bytes_AS_STRING(user));
}
/* If the algorithm is not md5 we have to use the API available from
* libpq 10. */
else {
#if PG_VERSION_NUM >= 100000
if (!conn) {
PyErr_SetString(ProgrammingError,
"password encryption (other than 'md5' algorithm)"
" requires a connection or cursor");
goto exit;
}
/* TODO: algo = None will block: forbid on async/green conn? */
encrypted = PQencryptPasswordConn(conn->pgconn,
Bytes_AS_STRING(password), Bytes_AS_STRING(user),
algorithm != Py_None ? Bytes_AS_STRING(algorithm) : NULL);
#else
PyErr_SetString(NotSupportedError,
"password encryption (other than 'md5' algorithm)"
" requires libpq 10");
goto exit;
#endif
}
if (encrypted) {
res = Text_FromUTF8(encrypted);
}
else {
const char *msg = PQerrorMessage(conn->pgconn);
PyErr_Format(ProgrammingError,
"password encryption failed: %s", msg ? msg : "no reason given");
goto exit;
}
exit:
if (encrypted) {
PQfreemem(encrypted);
}
Py_XDECREF(user);
Py_XDECREF(password);
Py_XDECREF(algorithm);
return res;
}
/* Fill the module's postgresql<->python encoding table */
static struct {
char *pgenc;
char *pyenc;
} enctable[] = {
{"ABC", "cp1258"},
{"ALT", "cp866"},
{"BIG5", "big5"},
{"EUC_CN", "euccn"},
{"EUC_JIS_2004", "euc_jis_2004"},
{"EUC_JP", "euc_jp"},
{"EUC_KR", "euc_kr"},
{"GB18030", "gb18030"},
{"GBK", "gbk"},
{"ISO_8859_1", "iso8859_1"},
{"ISO_8859_2", "iso8859_2"},
{"ISO_8859_3", "iso8859_3"},
{"ISO_8859_5", "iso8859_5"},
{"ISO_8859_6", "iso8859_6"},
{"ISO_8859_7", "iso8859_7"},
{"ISO_8859_8", "iso8859_8"},
{"ISO_8859_9", "iso8859_9"},
{"ISO_8859_10", "iso8859_10"},
{"ISO_8859_13", "iso8859_13"},
{"ISO_8859_14", "iso8859_14"},
{"ISO_8859_15", "iso8859_15"},
{"ISO_8859_16", "iso8859_16"},
{"JOHAB", "johab"},
{"KOI8", "koi8_r"},
{"KOI8R", "koi8_r"},
{"KOI8U", "koi8_u"},
{"LATIN1", "iso8859_1"},
{"LATIN2", "iso8859_2"},
{"LATIN3", "iso8859_3"},
{"LATIN4", "iso8859_4"},
{"LATIN5", "iso8859_9"},
{"LATIN6", "iso8859_10"},
{"LATIN7", "iso8859_13"},
{"LATIN8", "iso8859_14"},
{"LATIN9", "iso8859_15"},
{"LATIN10", "iso8859_16"},
{"Mskanji", "cp932"},
{"ShiftJIS", "cp932"},
{"SHIFT_JIS_2004", "shift_jis_2004"},
{"SJIS", "cp932"},
{"SQL_ASCII", "ascii"}, /* XXX this is wrong: SQL_ASCII means "no
* encoding" we should fix the unicode
* typecaster to return a str or bytes in Py3
*/
{"TCVN", "cp1258"},
{"TCVN5712", "cp1258"},
{"UHC", "cp949"},
{"UNICODE", "utf_8"}, /* Not valid in 8.2, backward compatibility */
{"UTF8", "utf_8"},
{"VSCII", "cp1258"},
{"WIN", "cp1251"},
{"WIN866", "cp866"},
{"WIN874", "cp874"},
{"WIN932", "cp932"},
{"WIN936", "gbk"},
{"WIN949", "cp949"},
{"WIN950", "cp950"},
{"WIN1250", "cp1250"},
{"WIN1251", "cp1251"},
{"WIN1252", "cp1252"},
{"WIN1253", "cp1253"},
{"WIN1254", "cp1254"},
{"WIN1255", "cp1255"},
{"WIN1256", "cp1256"},
{"WIN1257", "cp1257"},
{"WIN1258", "cp1258"},
{"Windows932", "cp932"},
{"Windows936", "gbk"},
{"Windows949", "cp949"},
{"Windows950", "cp950"},
/* those are missing from Python: */
/* {"EUC_TW", "?"}, */
/* {"MULE_INTERNAL", "?"}, */
{NULL, NULL}
};
/* Initialize the encodings table.
*
* Return 0 on success, else -1 and set an exception.
*/
RAISES_NEG static int
encodings_init(PyObject *module)
{
PyObject *value = NULL;
int i;
int rv = -1;
Dprintf("psycopgmodule: initializing encodings table");
if (!(psycoEncodings = PyDict_New())) { goto exit; }
Py_INCREF(psycoEncodings);
PyModule_AddObject(module, "encodings", psycoEncodings);
for (i = 0; enctable[i].pgenc != NULL; i++) {
if (!(value = Text_FromUTF8(enctable[i].pyenc))) { goto exit; }
if (0 > PyDict_SetItemString(
psycoEncodings, enctable[i].pgenc, value)) {
goto exit;
}
Py_CLEAR(value);
}
rv = 0;
exit:
Py_XDECREF(value);
return rv;
}
/* Initialize the module's exceptions and after that a dictionary with a full
set of exceptions. */
PyObject *Error, *Warning, *InterfaceError, *DatabaseError,
*InternalError, *OperationalError, *ProgrammingError,
*IntegrityError, *DataError, *NotSupportedError;
PyObject *QueryCanceledError, *TransactionRollbackError;
/* mapping between exception names and their PyObject */
static struct {
char *name;
PyObject **exc;
PyObject **base;
const char *docstr;
} exctable[] = {
{ "psycopg2.Error", &Error, NULL, Error_doc },
{ "psycopg2.Warning", &Warning, NULL, Warning_doc },
{ "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc },
{ "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc },
{ "psycopg2.InternalError", &InternalError, &DatabaseError, InternalError_doc },
{ "psycopg2.OperationalError", &OperationalError, &DatabaseError,
OperationalError_doc },
{ "psycopg2.ProgrammingError", &ProgrammingError, &DatabaseError,
ProgrammingError_doc },
{ "psycopg2.IntegrityError", &IntegrityError, &DatabaseError,
IntegrityError_doc },
{ "psycopg2.DataError", &DataError, &DatabaseError, DataError_doc },
{ "psycopg2.NotSupportedError", &NotSupportedError, &DatabaseError,
NotSupportedError_doc },
{ "psycopg2.extensions.QueryCanceledError", &QueryCanceledError,
&OperationalError, QueryCanceledError_doc },
{ "psycopg2.extensions.TransactionRollbackError",
&TransactionRollbackError, &OperationalError,
TransactionRollbackError_doc },
{NULL} /* Sentinel */
};
static int
psyco_errors_init(PyObject *module)
{
/* the names of the exceptions here reflect the organization of the
psycopg2 module and not the fact the the original error objects
live in _psycopg */
int i;
PyObject *dict = NULL;
PyObject *str = NULL;
int rv = -1;
Dprintf("psycopgmodule: initializing basic exceptions");
/* 'Error' has been defined elsewhere: only init the other classes */
Error = (PyObject *)&errorType;
for (i = 1; exctable[i].name; i++) {
if (!(dict = PyDict_New())) { goto exit; }
if (exctable[i].docstr) {
if (!(str = Text_FromUTF8(exctable[i].docstr))) { goto exit; }
if (0 > PyDict_SetItemString(dict, "__doc__", str)) { goto exit; }
Py_CLEAR(str);
}
/* can't put PyExc_StandardError in the static exctable:
* windows build will fail */
if (!(*exctable[i].exc = PyErr_NewException(
exctable[i].name,
exctable[i].base ? *exctable[i].base : PyExc_StandardError,
dict))) {
goto exit;
}
Py_CLEAR(dict);
}
for (i = 0; exctable[i].name; i++) {
char *name;
if (NULL == exctable[i].exc) { continue; }
/* the name is the part after the last dot */
name = strrchr(exctable[i].name, '.');
name = name ? name + 1 : exctable[i].name;
Py_INCREF(*exctable[i].exc);
if (0 > PyModule_AddObject(module, name, *exctable[i].exc)) {
goto exit;
}
}
rv = 0;
exit:
Py_XDECREF(str);
Py_XDECREF(dict);
return rv;
}
RAISES_NEG
static int
add_module_constants(PyObject *module)
{
PyObject *tmp;
Dprintf("psycopgmodule: initializing module constants");
if (0 > PyModule_AddStringConstant(module,
"__version__", xstr(PSYCOPG_VERSION)))
{ return -1; }
if (0 > PyModule_AddStringConstant(module,
"__doc__", "psycopg2 PostgreSQL driver"))
{ return -1; }
if (0 > PyModule_AddIntConstant(module,
"__libpq_version__", PG_VERSION_NUM))
{ return -1; }
if (0 > PyModule_AddObject(module,
"apilevel", tmp = Text_FromUTF8(APILEVEL)))
{
Py_XDECREF(tmp);
return -1;
}
if (0 > PyModule_AddObject(module,
"threadsafety", tmp = PyInt_FromLong(THREADSAFETY)))
{
Py_XDECREF(tmp);
return -1;
}
if (0 > PyModule_AddObject(module,
"paramstyle", tmp = Text_FromUTF8(PARAMSTYLE)))
{
Py_XDECREF(tmp);
return -1;
}
if (0 > PyModule_AddIntMacro(module, REPLICATION_PHYSICAL)) { return -1; }
if (0 > PyModule_AddIntMacro(module, REPLICATION_LOGICAL)) { return -1; }
return 0;
}
static struct {
char *name;
PyTypeObject *type;
} typetable[] = {
{ "connection", &connectionType },
{ "cursor", &cursorType },
{ "ReplicationConnection", &replicationConnectionType },
{ "ReplicationCursor", &replicationCursorType },
{ "ReplicationMessage", &replicationMessageType },
{ "ISQLQuote", &isqlquoteType },
{ "Column", &columnType },
{ "Notify", &notifyType },
{ "Xid", &xidType },
{ "ConnectionInfo", &connInfoType },
{ "Diagnostics", &diagnosticsType },
{ "AsIs", &asisType },
{ "Binary", &binaryType },
{ "Boolean", &pbooleanType },
{ "Decimal", &pdecimalType },
{ "Int", &pintType },
{ "Float", &pfloatType },
{ "List", &listType },
{ "QuotedString", &qstringType },
{ "lobject", &lobjectType },
{NULL} /* Sentinel */
};
RAISES_NEG
static int
add_module_types(PyObject *module)
{
int i;
Dprintf("psycopgmodule: initializing module types");
for (i = 0; typetable[i].name; i++) {
PyObject *type = (PyObject *)typetable[i].type;
Py_TYPE(typetable[i].type) = &PyType_Type;
if (0 > PyType_Ready(typetable[i].type)) { return -1; }
Py_INCREF(type);
if (0 > PyModule_AddObject(module, typetable[i].name, type)) {
return -1;
}
}
return 0;
}
RAISES_NEG static int
datetime_init(void)
{
PyObject *dt = NULL;
Dprintf("psycopgmodule: datetime module");
/* import python builtin datetime module, if available */
if (!(dt = PyImport_ImportModule("datetime"))) {
return -1;
}
Py_DECREF(dt);
/* Initialize the PyDateTimeAPI everywhere is used */
PyDateTime_IMPORT;
if (0 > psyco_adapter_datetime_init()) { return -1; }
if (0 > psyco_repl_curs_datetime_init()) { return -1; }
if (0 > psyco_replmsg_datetime_init()) { return -1; }
Py_TYPE(&pydatetimeType) = &PyType_Type;
if (PyType_Ready(&pydatetimeType) == -1) { return -1; }
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 (PyType_Ready(&mxdatetimeType) == -1) { 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;
dict = PyModule_GetDict(module);
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[] = {
{"_connect", (PyCFunction)psyco_connect,
METH_VARARGS|METH_KEYWORDS, psyco_connect_doc},
{"parse_dsn", (PyCFunction)psyco_parse_dsn,
METH_VARARGS|METH_KEYWORDS, psyco_parse_dsn_doc},
{"quote_ident", (PyCFunction)psyco_quote_ident,
METH_VARARGS|METH_KEYWORDS, psyco_quote_ident_doc},
{"adapt", (PyCFunction)psyco_microprotocols_adapt,
METH_VARARGS, psyco_microprotocols_adapt_doc},
{"register_type", (PyCFunction)psyco_register_type,
METH_VARARGS, psyco_register_type_doc},
{"new_type", (PyCFunction)typecast_from_python,
METH_VARARGS|METH_KEYWORDS, typecast_from_python_doc},
{"new_array_type", (PyCFunction)typecast_array_from_python,
METH_VARARGS|METH_KEYWORDS, typecast_array_from_python_doc},
{"libpq_version", (PyCFunction)psyco_libpq_version,
METH_NOARGS, psyco_libpq_version_doc},
{"Date", (PyCFunction)psyco_Date,
METH_VARARGS, psyco_Date_doc},
{"Time", (PyCFunction)psyco_Time,
METH_VARARGS, psyco_Time_doc},
{"Timestamp", (PyCFunction)psyco_Timestamp,
METH_VARARGS, psyco_Timestamp_doc},
{"DateFromTicks", (PyCFunction)psyco_DateFromTicks,
METH_VARARGS, psyco_DateFromTicks_doc},
{"TimeFromTicks", (PyCFunction)psyco_TimeFromTicks,
METH_VARARGS, psyco_TimeFromTicks_doc},
{"TimestampFromTicks", (PyCFunction)psyco_TimestampFromTicks,
METH_VARARGS, psyco_TimestampFromTicks_doc},
{"DateFromPy", (PyCFunction)psyco_DateFromPy,
METH_VARARGS, psyco_DateFromPy_doc},
{"TimeFromPy", (PyCFunction)psyco_TimeFromPy,
METH_VARARGS, psyco_TimeFromPy_doc},
{"TimestampFromPy", (PyCFunction)psyco_TimestampFromPy,
METH_VARARGS, psyco_TimestampFromPy_doc},
{"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,
METH_NOARGS, psyco_get_wait_callback_doc},
{"encrypt_password", (PyCFunction)psyco_encrypt_password,
METH_VARARGS|METH_KEYWORDS, psyco_encrypt_password_doc},
{NULL, NULL, 0, NULL} /* Sentinel */
};
#if PY_MAJOR_VERSION > 2
static struct PyModuleDef psycopgmodule = {
PyModuleDef_HEAD_INIT,
"_psycopg",
NULL,
-1,
psycopgMethods,
NULL,
NULL,
NULL,
NULL
};
#endif
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
INIT_MODULE(_psycopg)(void)
{
PyObject *module = NULL, *dict;
#ifdef PSYCOPG_DEBUG
if (getenv("PSYCOPG_DEBUG"))
psycopg_debug_enabled = 1;
#endif
Dprintf("psycopgmodule: initializing psycopg %s", xstr(PSYCOPG_VERSION));
/* initialize the types not exposed to the module */
Py_TYPE(&typecastType) = &PyType_Type;
if (PyType_Ready(&typecastType) == -1) goto exit;
Py_TYPE(&chunkType) = &PyType_Type;
if (PyType_Ready(&chunkType) == -1) goto exit;
Py_TYPE(&errorType) = &PyType_Type;
errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
if (PyType_Ready(&errorType) == -1) goto exit;
/* initialize libcrypto threading callbacks */
psyco_libcrypto_threads_init();
/* initialize the module and grab module's dictionary */
#if PY_MAJOR_VERSION < 3
module = Py_InitModule("_psycopg", psycopgMethods);
#else
module = PyModule_Create(&psycopgmodule);
#endif
if (!module) { goto exit; }
/* other mixed initializations of module-level variables */
if (!(psyco_null = Bytes_FromString("NULL"))) { goto exit; }
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; }
/* encodings dictionary in module dictionary */
if (0 > encodings_init(module)) { goto exit; }
dict = PyModule_GetDict(module);
/* initialize default set of typecasters */
if (0 != typecast_init(dict)) { goto exit; }
/* initialize microprotocols layer */
microprotocols_init(dict);
if (0 != psyco_adapters_init(dict)) { goto exit; }
/* create a standard set of exceptions and add them to the module's dict */
if (0 != psyco_errors_init(module)) { goto exit; }
Dprintf("psycopgmodule: module initialization complete");
exit:
#if PY_MAJOR_VERSION > 2
return module;
#else
return;
#endif
}