1031 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1031 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* psycopgmodule.c - psycopg module (will import other C classes)
 | |
|  *
 | |
|  * Copyright (C) 2003-2019 Federico Di Gregorio <fog@debian.org>
 | |
|  * Copyright (C) 2020-2021 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/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"
 | |
| 
 | |
| /* some module-level variables, like the datetime module */
 | |
| #include <datetime.h>
 | |
| #include "psycopg/adapter_datetime.h"
 | |
| 
 | |
| HIDDEN PyObject *psycoEncodings = NULL;
 | |
| HIDDEN PyObject *sqlstate_errors = 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 parse_dsn_doc \
 | |
| "parse_dsn(dsn) -> dict -- parse a connection string into parameters"
 | |
| 
 | |
| static PyObject *
 | |
| 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 = psyco_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 = psyco_dict_from_conninfo_options(options, /* include_password = */ 1);
 | |
| 
 | |
| exit:
 | |
|     PQconninfoFree(options);    /* safe on null */
 | |
|     Py_XDECREF(dsn);
 | |
| 
 | |
|     return res;
 | |
| }
 | |
| 
 | |
| 
 | |
| #define 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 *
 | |
| 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 = psyco_ensure_bytes(ident))) { goto exit; }
 | |
| 
 | |
|     if (!(quoted = psyco_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 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 *
 | |
| 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
 | |
| 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
 | |
| adapters_init(PyObject *module)
 | |
| {
 | |
|     PyObject *dict = NULL, *obj = NULL;
 | |
|     int rv = -1;
 | |
| 
 | |
|     if (0 > microprotocols_init(module)) { goto exit; }
 | |
| 
 | |
|     Dprintf("psycopgmodule: initializing adapters");
 | |
| 
 | |
|     if (0 > microprotocols_add(&PyFloat_Type, NULL, (PyObject*)&pfloatType)) {
 | |
|         goto exit;
 | |
|     }
 | |
|     if (0 > microprotocols_add(&PyLong_Type, NULL, (PyObject*)&pintType)) {
 | |
|         goto exit;
 | |
|     }
 | |
|     if (0 > microprotocols_add(&PyBool_Type, NULL, (PyObject*)&pbooleanType)) {
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     /* strings */
 | |
|     if (0 > microprotocols_add(&PyUnicode_Type, NULL, (PyObject*)&qstringType)) {
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     /* binary */
 | |
|     if (0 > microprotocols_add(&PyBytes_Type, NULL, (PyObject*)&binaryType)) {
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     if (0 > microprotocols_add(&PyByteArray_Type, NULL, (PyObject*)&binaryType)) {
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     if (0 > microprotocols_add(&PyMemoryView_Type, NULL, (PyObject*)&binaryType)) {
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     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 (!(dict = PyModule_GetDict(module))) { goto exit; }
 | |
| 
 | |
|     if (!(obj = PyMapping_GetItemString(dict, "DateFromPy"))) { goto exit; }
 | |
|     if (0 > microprotocols_add(PyDateTimeAPI->DateType, NULL, obj)) { goto exit; }
 | |
|     Py_CLEAR(obj);
 | |
| 
 | |
|     if (!(obj = PyMapping_GetItemString(dict, "TimeFromPy"))) { goto exit; }
 | |
|     if (0 > microprotocols_add(PyDateTimeAPI->TimeType, NULL, obj)) { goto exit; }
 | |
|     Py_CLEAR(obj);
 | |
| 
 | |
|     if (!(obj = PyMapping_GetItemString(dict, "TimestampFromPy"))) { goto exit; }
 | |
|     if (0 > microprotocols_add(PyDateTimeAPI->DateTimeType, NULL, obj)) { goto exit; }
 | |
|     Py_CLEAR(obj);
 | |
| 
 | |
|     if (!(obj = PyMapping_GetItemString(dict, "IntervalFromPy"))) { goto exit; }
 | |
|     if (0 > microprotocols_add(PyDateTimeAPI->DeltaType, NULL, obj)) { goto exit; }
 | |
|     Py_CLEAR(obj);
 | |
| 
 | |
|     /* Success! */
 | |
|     rv = 0;
 | |
| 
 | |
| exit:
 | |
|     Py_XDECREF(obj);
 | |
| 
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| #define libpq_version_doc "Query actual libpq version loaded."
 | |
| 
 | |
| static PyObject*
 | |
| libpq_version(PyObject *self, PyObject *dummy)
 | |
| {
 | |
|     return PyInt_FromLong(PQlibVersion());
 | |
| }
 | |
| 
 | |
| /* encrypt_password - Prepare the encrypted password form */
 | |
| #define encrypt_password_doc \
 | |
| "encrypt_password(password, user, [scope], [algorithm]) -- Prepares the encrypted form of a PostgreSQL password.\n\n"
 | |
| 
 | |
| static PyObject *
 | |
| 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 = psyco_ensure_bytes(user))) { goto exit; }
 | |
|     if (!(password = psyco_ensure_bytes(password))) { goto exit; }
 | |
|     if (algorithm != Py_None) {
 | |
|         if (!(algorithm = psyco_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) {
 | |
|         Dprintf("encodings_init(): already called");
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     if (!(psycoEncodings = PyDict_New())) { goto exit; }
 | |
|     Py_INCREF(psycoEncodings);
 | |
|     if (0 > PyModule_AddObject(module, "encodings", psycoEncodings)) {
 | |
|         Py_DECREF(psycoEncodings);
 | |
|         goto exit;
 | |
|     }
 | |
| 
 | |
|     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 */
 | |
| };
 | |
| 
 | |
| 
 | |
| RAISES_NEG static int
 | |
| basic_errors_init(PyObject *module)
 | |
| {
 | |
|     /* the names of the exceptions here reflect the organization of the
 | |
|        psycopg2 module and not the fact the original error objects live in
 | |
|        _psycopg */
 | |
| 
 | |
|     int i;
 | |
|     PyObject *dict = NULL;
 | |
|     PyObject *str = NULL;
 | |
|     PyObject *errmodule = 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);
 | |
|     }
 | |
| 
 | |
|     if (!(errmodule = PyImport_ImportModule("psycopg2.errors"))) {
 | |
|         /* don't inject the exceptions into the errors module */
 | |
|         PyErr_Clear();
 | |
|     }
 | |
| 
 | |
|     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)) {
 | |
|             Py_DECREF(*exctable[i].exc);
 | |
|             goto exit;
 | |
|         }
 | |
|         if (errmodule) {
 | |
|             Py_INCREF(*exctable[i].exc);
 | |
|             if (0 > PyModule_AddObject(errmodule, name, *exctable[i].exc)) {
 | |
|                 Py_DECREF(*exctable[i].exc);
 | |
|                 goto exit;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     rv = 0;
 | |
| 
 | |
| exit:
 | |
|     Py_XDECREF(errmodule);
 | |
|     Py_XDECREF(str);
 | |
|     Py_XDECREF(dict);
 | |
|     return rv;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* mapping between sqlstate and exception name */
 | |
| static struct {
 | |
|     char *sqlstate;
 | |
|     char *name;
 | |
| } sqlstate_table[] = {
 | |
| #include "sqlstate_errors.h"
 | |
|     {NULL}  /* Sentinel */
 | |
| };
 | |
| 
 | |
| 
 | |
| RAISES_NEG static int
 | |
| sqlstate_errors_init(PyObject *module)
 | |
| {
 | |
|     int i;
 | |
|     char namebuf[120];
 | |
|     char prefix[] = "psycopg2.errors.";
 | |
|     char *suffix;
 | |
|     size_t bufsize;
 | |
|     PyObject *exc = NULL;
 | |
|     PyObject *errmodule = NULL;
 | |
|     int rv = -1;
 | |
| 
 | |
|     Dprintf("psycopgmodule: initializing sqlstate exceptions");
 | |
| 
 | |
|     if (sqlstate_errors) {
 | |
| 		Dprintf("sqlstate_errors_init(): already called");
 | |
|         return 0;
 | |
|     }
 | |
|     if (!(errmodule = PyImport_ImportModule("psycopg2.errors"))) {
 | |
|         /* don't inject the exceptions into the errors module */
 | |
|         PyErr_Clear();
 | |
|     }
 | |
|     if (!(sqlstate_errors = PyDict_New())) {
 | |
|         goto exit;
 | |
|     }
 | |
|     Py_INCREF(sqlstate_errors);
 | |
|     if (0 > PyModule_AddObject(module, "sqlstate_errors", sqlstate_errors)) {
 | |
|         Py_DECREF(sqlstate_errors);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     strcpy(namebuf, prefix);
 | |
|     suffix = namebuf + sizeof(prefix) - 1;
 | |
|     bufsize = sizeof(namebuf) - sizeof(prefix) - 1;
 | |
|     /* If this 0 gets deleted the buffer was too small. */
 | |
|     namebuf[sizeof(namebuf) - 1] = '\0';
 | |
| 
 | |
|     for (i = 0; sqlstate_table[i].sqlstate; i++) {
 | |
|         PyObject *base;
 | |
| 
 | |
|         base = base_exception_from_sqlstate(sqlstate_table[i].sqlstate);
 | |
|         strncpy(suffix, sqlstate_table[i].name, bufsize);
 | |
|         if (namebuf[sizeof(namebuf) - 1] != '\0') {
 | |
|             PyErr_SetString(
 | |
|                 PyExc_SystemError, "sqlstate_errors_init(): buffer too small");
 | |
|             goto exit;
 | |
|         }
 | |
|         if (!(exc = PyErr_NewException(namebuf, base, NULL))) {
 | |
|             goto exit;
 | |
|         }
 | |
|         if (0 > PyDict_SetItemString(
 | |
|                 sqlstate_errors, sqlstate_table[i].sqlstate, exc)) {
 | |
|             goto exit;
 | |
|         }
 | |
| 
 | |
|         /* Expose the exceptions to psycopg2.errors */
 | |
|         if (errmodule) {
 | |
|             if (0 > PyModule_AddObject(
 | |
|                     errmodule, sqlstate_table[i].name, exc)) {
 | |
|                 goto exit;
 | |
|             }
 | |
|             else {
 | |
|                 exc = NULL;     /* ref stolen by the module */
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             Py_CLEAR(exc);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     rv = 0;
 | |
| 
 | |
| exit:
 | |
|     Py_XDECREF(errmodule);
 | |
|     Py_XDECREF(exc);
 | |
|     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", ¬ifyType },
 | |
|     { "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_SET_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)) {
 | |
|             Py_DECREF(type);
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| RAISES_NEG static int
 | |
| datetime_init(void)
 | |
| {
 | |
|     PyObject *dt = NULL;
 | |
| 
 | |
|     Dprintf("psycopgmodule: initializing 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 > adapter_datetime_init()) { return -1; }
 | |
|     if (0 > repl_curs_datetime_init()) { return -1; }
 | |
|     if (0 > replmsg_datetime_init()) { return -1; }
 | |
| 
 | |
|     Py_SET_TYPE(&pydatetimeType, &PyType_Type);
 | |
|     if (0 > PyType_Ready(&pydatetimeType)) { return -1; }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /** method table and module initialization **/
 | |
| 
 | |
| static PyMethodDef psycopgMethods[] = {
 | |
|     {"_connect",  (PyCFunction)psyco_connect,
 | |
|      METH_VARARGS|METH_KEYWORDS, psyco_connect_doc},
 | |
|     {"parse_dsn",  (PyCFunction)parse_dsn,
 | |
|      METH_VARARGS|METH_KEYWORDS, parse_dsn_doc},
 | |
|     {"quote_ident", (PyCFunction)quote_ident,
 | |
|      METH_VARARGS|METH_KEYWORDS, quote_ident_doc},
 | |
|     {"adapt",  (PyCFunction)psyco_microprotocols_adapt,
 | |
|      METH_VARARGS, psyco_microprotocols_adapt_doc},
 | |
| 
 | |
|     {"register_type", (PyCFunction)register_type,
 | |
|      METH_VARARGS, 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)libpq_version,
 | |
|      METH_NOARGS, 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},
 | |
| 
 | |
|     {"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)encrypt_password,
 | |
|      METH_VARARGS|METH_KEYWORDS, encrypt_password_doc},
 | |
| 
 | |
|     {NULL, NULL, 0, NULL}        /* Sentinel */
 | |
| };
 | |
| 
 | |
| static struct PyModuleDef psycopgmodule = {
 | |
|         PyModuleDef_HEAD_INIT,
 | |
|         "_psycopg",
 | |
|         NULL,
 | |
|         -1,
 | |
|         psycopgMethods,
 | |
|         NULL,
 | |
|         NULL,
 | |
|         NULL,
 | |
|         NULL
 | |
| };
 | |
| 
 | |
| #ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
 | |
| #define PyMODINIT_FUNC void
 | |
| #endif
 | |
| PyMODINIT_FUNC
 | |
| INIT_MODULE(_psycopg)(void)
 | |
| {
 | |
|     PyObject *module = NULL;
 | |
| 
 | |
| #ifdef PSYCOPG_DEBUG
 | |
|     if (getenv("PSYCOPG_DEBUG"))
 | |
|         psycopg_debug_enabled = 1;
 | |
| #endif
 | |
| 
 | |
|     Dprintf("psycopgmodule: initializing psycopg %s", xstr(PSYCOPG_VERSION));
 | |
| 
 | |
|     /* initialize libcrypto threading callbacks */
 | |
|     libcrypto_threads_init();
 | |
| 
 | |
|     /* initialize types and objects not exposed to the module */
 | |
|     Py_SET_TYPE(&typecastType, &PyType_Type);
 | |
|     if (0 > PyType_Ready(&typecastType)) { goto exit; }
 | |
| 
 | |
|     Py_SET_TYPE(&chunkType, &PyType_Type);
 | |
|     if (0 > PyType_Ready(&chunkType)) { goto exit; }
 | |
| 
 | |
|     Py_SET_TYPE(&errorType, &PyType_Type);
 | |
|     errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
 | |
|     if (0 > PyType_Ready(&errorType)) { goto exit; }
 | |
| 
 | |
|     if (!(psyco_null = Bytes_FromString("NULL"))) { goto exit; }
 | |
| 
 | |
|     /* initialize the module */
 | |
|     module = PyModule_Create(&psycopgmodule);
 | |
|     if (!module) { 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 > encodings_init(module)) { goto exit; }
 | |
|     if (0 > typecast_init(module)) { goto exit; }
 | |
|     if (0 > adapters_init(module)) { goto exit; }
 | |
|     if (0 > basic_errors_init(module)) { goto exit; }
 | |
|     if (0 > sqlstate_errors_init(module)) { goto exit; }
 | |
| 
 | |
|     Dprintf("psycopgmodule: module initialization complete");
 | |
| 
 | |
| exit:
 | |
|     return module;
 | |
| }
 | 
