mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 08:56:34 +03:00
Merge branch 'master' into replication-protocol
Conflicts: tests/testconfig.py
This commit is contained in:
commit
2a4d6027a4
|
@ -2,10 +2,10 @@ recursive-include psycopg *.c *.h *.manifest
|
|||
recursive-include lib *.py
|
||||
recursive-include tests *.py
|
||||
recursive-include examples *.py somehackers.jpg whereareyou.jpg
|
||||
recursive-include doc README SUCCESS COPYING.LESSER pep-0249.txt
|
||||
recursive-include doc Makefile requirements.txt
|
||||
include doc/README.rst doc/SUCCESS doc/COPYING.LESSER doc/pep-0249.txt
|
||||
include doc/Makefile doc/requirements.txt
|
||||
recursive-include doc/src *.rst *.py *.css Makefile
|
||||
recursive-include scripts *.py *.sh
|
||||
include scripts/maketypes.sh scripts/buildtypes.py
|
||||
include AUTHORS README.rst INSTALL LICENSE NEWS
|
||||
include PKG-INFO MANIFEST.in MANIFEST setup.py setup.cfg Makefile
|
||||
include MANIFEST.in setup.py setup.cfg Makefile
|
||||
|
|
9
Makefile
9
Makefile
|
@ -92,14 +92,9 @@ $(PACKAGE)/tests/%.py: tests/%.py
|
|||
$(PYTHON) setup.py build_py $(BUILD_OPT)
|
||||
touch $@
|
||||
|
||||
$(SDIST): MANIFEST $(SOURCE)
|
||||
$(SDIST): $(SOURCE)
|
||||
$(PYTHON) setup.py sdist $(SDIST_OPT)
|
||||
|
||||
MANIFEST: MANIFEST.in $(SOURCE)
|
||||
# Run twice as MANIFEST.in includes MANIFEST
|
||||
$(PYTHON) setup.py sdist --manifest-only
|
||||
$(PYTHON) setup.py sdist --manifest-only
|
||||
|
||||
# docs depend on the build as it partly use introspection.
|
||||
doc/html/genindex.html: $(PLATLIB) $(PURELIB) $(SOURCE_DOC)
|
||||
$(MAKE) -C doc html
|
||||
|
@ -111,5 +106,5 @@ doc/docs.zip: doc/html/genindex.html
|
|||
(cd doc/html && zip -r ../docs.zip *)
|
||||
|
||||
clean:
|
||||
rm -rf build MANIFEST
|
||||
rm -rf build
|
||||
$(MAKE) -C doc clean
|
||||
|
|
13
NEWS
13
NEWS
|
@ -18,19 +18,28 @@ New features:
|
|||
customized replacing them with any object exposing an `!append()` method
|
||||
(:ticket:`#326`).
|
||||
- Added `~psycopg2.extensions.quote_ident()` function (:ticket:`#359`).
|
||||
- Added `~connection.get_dsn_parameters()` connection method (:ticket:`#364`).
|
||||
|
||||
|
||||
What's new in psycopg 2.6.2
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Report the server response status on errors (such as :ticket:`#281`).
|
||||
- Raise `!NotSupportedError` on unhandled server response status
|
||||
(:ticket:`#352`).
|
||||
- Allow overriding string adapter encoding with no connection (:ticket:`#331`).
|
||||
- The `~psycopg2.extras.wait_select` callback allows interrupting a
|
||||
long-running query in an interactive shell using :kbd:`Ctrl-C`
|
||||
(:ticket:`#333`).
|
||||
- Raise `!NotSupportedError` on unhandled server response status
|
||||
(:ticket:`#352`).
|
||||
- Fixed `!PersistentConnectionPool` on Python 3 (:ticket:`#348`).
|
||||
- Fixed segfault on `repr()` of an unitialized connection (:ticket:`#361`).
|
||||
- Allow adapting bytes using QuotedString on Python 3 too (:ticket:`#365`).
|
||||
- Added support for setuptools/wheel (:ticket:`#370`).
|
||||
- Fix build on Windows with Python 3.5, VS 2015 (:ticket:`#380`).
|
||||
- Fixed `!errorcodes.lookup` initialization thread-safety (:ticket:`#382`).
|
||||
- Fixed `!read()` exception propagation in copy_from (:ticket:`#412`).
|
||||
- Fixed possible NULL TZ decref (:ticket:`#424`).
|
||||
- `~psycopg2.errorcodes` map updated to PostgreSQL 9.5.
|
||||
|
||||
|
||||
What's new in psycopg 2.6.1
|
||||
|
|
|
@ -47,7 +47,7 @@ it is the class where query building, execution and result type-casting into
|
|||
Python variables happens.
|
||||
|
||||
The `~psycopg2.extras` module contains several examples of :ref:`connection
|
||||
and cursor sublcasses <cursor-subclasses>`.
|
||||
and cursor subclasses <cursor-subclasses>`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
@ -42,9 +42,7 @@ master_doc = 'index'
|
|||
|
||||
# General information about the project.
|
||||
project = u'Psycopg'
|
||||
from datetime import date
|
||||
year = date.today().year
|
||||
copyright = u'2001-%s, Federico Di Gregorio, Daniele Varrazzo' % year
|
||||
copyright = u'2001-2016, Federico Di Gregorio, Daniele Varrazzo'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
|
|
@ -568,6 +568,29 @@ The ``connection`` class
|
|||
.. versionadded:: 2.0.12
|
||||
|
||||
|
||||
.. index::
|
||||
pair: Connection; Parameters
|
||||
|
||||
.. method:: get_dsn_parameters()
|
||||
|
||||
Get the effective dsn parameters for the connection as a dictionary.
|
||||
|
||||
The *password* parameter is removed from the result.
|
||||
|
||||
Example::
|
||||
|
||||
>>> conn.get_dsn_parameters()
|
||||
{'dbname': 'test', 'user': 'postgres', 'port': '5432', 'sslmode': 'prefer'}
|
||||
|
||||
Requires libpq >= 9.3.
|
||||
|
||||
.. seealso:: libpq docs for `PQconninfo()`__ for details.
|
||||
|
||||
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFO
|
||||
|
||||
.. versionadded:: 2.7
|
||||
|
||||
|
||||
.. index::
|
||||
pair: Transaction; Status
|
||||
|
||||
|
|
|
@ -494,6 +494,9 @@ The ``cursor`` class
|
|||
|
||||
.. rubric:: COPY-related methods
|
||||
|
||||
Efficiently copy data from file-like objects to the database and back. See
|
||||
:ref:`copy` for an overview.
|
||||
|
||||
.. extension::
|
||||
|
||||
The :sql:`COPY` command is a PostgreSQL extension to the SQL standard.
|
||||
|
@ -502,7 +505,7 @@ The ``cursor`` class
|
|||
.. method:: copy_from(file, table, sep='\\t', null='\\\\N', size=8192, columns=None)
|
||||
|
||||
Read data *from* the file-like object *file* appending them to
|
||||
the table named *table*. See :ref:`copy` for an overview.
|
||||
the table named *table*.
|
||||
|
||||
:param file: file-like object to read data from. It must have both
|
||||
`!read()` and `!readline()` methods.
|
||||
|
|
|
@ -18,7 +18,7 @@ The current `!psycopg2` implementation supports:
|
|||
NOTE: keep consistent with setup.py and the /features/ page.
|
||||
|
||||
- Python 2 versions from 2.5 to 2.7
|
||||
- Python 3 versions from 3.1 to 3.4
|
||||
- Python 3 versions from 3.1 to 3.5
|
||||
- PostgreSQL versions from 7.4 to 9.4
|
||||
|
||||
.. _PostgreSQL: http://www.postgresql.org/
|
||||
|
@ -51,6 +51,16 @@ extension packages, *above all if you are a Windows or a Mac OS user*, please
|
|||
use a pre-compiled package and go straight to the :ref:`module usage <usage>`
|
||||
avoid bothering with the gory details.
|
||||
|
||||
.. note::
|
||||
|
||||
Regardless of the way `!psycopg2` is installed, at runtime it will need to
|
||||
use the libpq_ library. `!psycopg2` relies on the host OS to find the
|
||||
library file (usually ``libpq.so`` or ``libpq.dll``): if the library is
|
||||
installed in a standard location there is usually no problem; if the
|
||||
library is in a non-standard location you will have to tell somehow
|
||||
psycopg how to find it, which is OS-dependent (for instance setting a
|
||||
suitable :envvar:`LD_LIBRARY_PATH` on Linux).
|
||||
|
||||
|
||||
|
||||
.. _install-from-package:
|
||||
|
@ -95,7 +105,17 @@ Install from a package
|
|||
pair: Install; Windows
|
||||
|
||||
**Microsoft Windows**
|
||||
Jason Erickson maintains a packaged `Windows port of Psycopg`__ with
|
||||
There are two options to install a precompiled `psycopg2` package under windows:
|
||||
|
||||
**Option 1:** Using `pip`__ (Included in python 2.7.9+ and python 3.4+)
|
||||
and a binary wheel package. Launch windows' command prompt (`cmd.exe`)
|
||||
and execute the following command::
|
||||
|
||||
pip install psycopg2
|
||||
|
||||
.. __: https://pip.pypa.io/en/stable/installing/
|
||||
|
||||
**Option 2:** Jason Erickson maintains a packaged `Windows port of Psycopg`__ with
|
||||
installation executable. Download. Double click. Done.
|
||||
|
||||
.. __: http://www.stickpeople.com/projects/python/win-psycopg/
|
||||
|
|
|
@ -864,11 +864,19 @@ Using COPY TO and COPY FROM
|
|||
|
||||
Psycopg `cursor` objects provide an interface to the efficient
|
||||
PostgreSQL |COPY|__ command to move data from files to tables and back.
|
||||
|
||||
Currently no adaptation is provided between Python and PostgreSQL types on
|
||||
|COPY|: the file can be any Python file-like object but its format must be in
|
||||
the format accepted by `PostgreSQL COPY command`__ (data fromat, escaped
|
||||
characters, etc).
|
||||
|
||||
.. __: COPY_
|
||||
|
||||
The methods exposed are:
|
||||
|
||||
`~cursor.copy_from()`
|
||||
Reads data *from* a file-like object appending them to a database table
|
||||
(:sql:`COPY table FROM file` syntax). The source file must have both
|
||||
(:sql:`COPY table FROM file` syntax). The source file must provide both
|
||||
`!read()` and `!readline()` method.
|
||||
|
||||
`~cursor.copy_to()`
|
||||
|
|
|
@ -199,6 +199,8 @@ INVALID_ESCAPE_SEQUENCE = '22025'
|
|||
STRING_DATA_LENGTH_MISMATCH = '22026'
|
||||
TRIM_ERROR = '22027'
|
||||
ARRAY_SUBSCRIPT_ERROR = '2202E'
|
||||
INVALID_TABLESAMPLE_REPEAT = '2202G'
|
||||
INVALID_TABLESAMPLE_ARGUMENT = '2202H'
|
||||
FLOATING_POINT_EXCEPTION = '22P01'
|
||||
INVALID_TEXT_REPRESENTATION = '22P02'
|
||||
INVALID_BINARY_REPRESENTATION = '22P03'
|
||||
|
@ -271,6 +273,7 @@ INVALID_SQLSTATE_RETURNED = '39001'
|
|||
NULL_VALUE_NOT_ALLOWED = '39004'
|
||||
TRIGGER_PROTOCOL_VIOLATED = '39P01'
|
||||
SRF_PROTOCOL_VIOLATED = '39P02'
|
||||
EVENT_TRIGGER_PROTOCOL_VIOLATED = '39P03'
|
||||
|
||||
# Class 3B - Savepoint Exception
|
||||
SAVEPOINT_EXCEPTION = '3B000'
|
||||
|
@ -408,6 +411,7 @@ PLPGSQL_ERROR = 'P0000'
|
|||
RAISE_EXCEPTION = 'P0001'
|
||||
NO_DATA_FOUND = 'P0002'
|
||||
TOO_MANY_ROWS = 'P0003'
|
||||
ASSERT_FAILURE = 'P0004'
|
||||
|
||||
# Class XX - Internal Error
|
||||
INTERNAL_ERROR = 'XX000'
|
||||
|
|
|
@ -451,7 +451,7 @@ psyco_TimestampFromTicks(PyObject *self, PyObject *args)
|
|||
tz);
|
||||
|
||||
exit:
|
||||
Py_DECREF(tz);
|
||||
Py_XDECREF(tz);
|
||||
Py_XDECREF(m);
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -36,44 +36,56 @@ static const char *default_encoding = "latin1";
|
|||
|
||||
/* qstring_quote - do the quote process on plain and unicode strings */
|
||||
|
||||
const char *
|
||||
_qstring_get_encoding(qstringObject *self)
|
||||
{
|
||||
/* if the wrapped object is an unicode object we can encode it to match
|
||||
conn->encoding but if the encoding is not specified we don't know what
|
||||
to do and we raise an exception */
|
||||
if (self->conn) {
|
||||
return self->conn->codec;
|
||||
}
|
||||
else {
|
||||
return self->encoding ? self->encoding : default_encoding;
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
qstring_quote(qstringObject *self)
|
||||
{
|
||||
PyObject *str = NULL;
|
||||
char *s, *buffer = NULL;
|
||||
Py_ssize_t len, qlen;
|
||||
const char *encoding = default_encoding;
|
||||
const char *encoding;
|
||||
PyObject *rv = NULL;
|
||||
|
||||
/* if the wrapped object is an unicode object we can encode it to match
|
||||
conn->encoding but if the encoding is not specified we don't know what
|
||||
to do and we raise an exception */
|
||||
if (self->conn) {
|
||||
encoding = self->conn->codec;
|
||||
}
|
||||
|
||||
encoding = _qstring_get_encoding(self);
|
||||
Dprintf("qstring_quote: encoding to %s", encoding);
|
||||
|
||||
if (PyUnicode_Check(self->wrapped) && encoding) {
|
||||
if (PyUnicode_Check(self->wrapped)) {
|
||||
if (encoding) {
|
||||
str = PyUnicode_AsEncodedString(self->wrapped, encoding, NULL);
|
||||
Dprintf("qstring_quote: got encoded object at %p", str);
|
||||
if (str == NULL) goto exit;
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"missing encoding to encode unicode object");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
/* if the wrapped object is a simple string, we don't know how to
|
||||
/* if the wrapped object is a binary string, we don't know how to
|
||||
(re)encode it, so we pass it as-is */
|
||||
else if (PyString_Check(self->wrapped)) {
|
||||
else if (Bytes_Check(self->wrapped)) {
|
||||
str = self->wrapped;
|
||||
/* INCREF to make it ref-wise identical to unicode one */
|
||||
Py_INCREF(str);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* if the wrapped object is not a string, this is an error */
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"can't quote non-string object (or missing encoding)");
|
||||
PyErr_SetString(PyExc_TypeError, "can't quote non-string object");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
@ -150,13 +162,32 @@ qstring_conform(qstringObject *self, PyObject *args)
|
|||
static PyObject *
|
||||
qstring_get_encoding(qstringObject *self)
|
||||
{
|
||||
const char *encoding = default_encoding;
|
||||
|
||||
if (self->conn) {
|
||||
encoding = self->conn->codec;
|
||||
const char *encoding;
|
||||
encoding = _qstring_get_encoding(self);
|
||||
return Text_FromUTF8(encoding);
|
||||
}
|
||||
|
||||
return Text_FromUTF8(encoding);
|
||||
static int
|
||||
qstring_set_encoding(qstringObject *self, PyObject *pyenc)
|
||||
{
|
||||
int rv = -1;
|
||||
const char *tmp;
|
||||
char *cenc;
|
||||
|
||||
/* get a C copy of the encoding (which may come from unicode) */
|
||||
Py_INCREF(pyenc);
|
||||
if (!(pyenc = psycopg_ensure_bytes(pyenc))) { goto exit; }
|
||||
if (!(tmp = Bytes_AsString(pyenc))) { goto exit; }
|
||||
if (0 > psycopg_strdup(&cenc, tmp, 0)) { goto exit; }
|
||||
|
||||
Dprintf("qstring_set_encoding: encoding set to %s", cenc);
|
||||
PyMem_Free((void *)self->encoding);
|
||||
self->encoding = cenc;
|
||||
rv = 0;
|
||||
|
||||
exit:
|
||||
Py_XDECREF(pyenc);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/** the QuotedString object **/
|
||||
|
@ -183,7 +214,7 @@ static PyMethodDef qstringObject_methods[] = {
|
|||
static PyGetSetDef qstringObject_getsets[] = {
|
||||
{ "encoding",
|
||||
(getter)qstring_get_encoding,
|
||||
(setter)NULL,
|
||||
(setter)qstring_set_encoding,
|
||||
"current encoding of the adapter" },
|
||||
{NULL}
|
||||
};
|
||||
|
@ -216,6 +247,7 @@ qstring_dealloc(PyObject* obj)
|
|||
Py_CLEAR(self->wrapped);
|
||||
Py_CLEAR(self->buffer);
|
||||
Py_CLEAR(self->conn);
|
||||
PyMem_Free((void *)self->encoding);
|
||||
|
||||
Dprintf("qstring_dealloc: deleted qstring object at %p, refcnt = "
|
||||
FORMAT_CODE_PY_SSIZE_T,
|
||||
|
|
|
@ -39,6 +39,9 @@ typedef struct {
|
|||
PyObject *buffer;
|
||||
|
||||
connectionObject *conn;
|
||||
|
||||
const char *encoding;
|
||||
|
||||
} qstringObject;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -733,6 +733,37 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
|
|||
return conn_text_from_chars(self, val);
|
||||
}
|
||||
|
||||
/* get_dsn_parameters method - Get connection parameters */
|
||||
|
||||
#define psyco_conn_get_dsn_parameters_doc \
|
||||
"get_dsn_parameters() -- Get effective connection parameters.\n\n"
|
||||
|
||||
static PyObject *
|
||||
psyco_conn_get_dsn_parameters(connectionObject *self)
|
||||
{
|
||||
#if PG_VERSION_NUM >= 90300
|
||||
PyObject *res = NULL;
|
||||
PQconninfoOption *options = NULL;
|
||||
|
||||
EXC_IF_CONN_CLOSED(self);
|
||||
|
||||
if (!(options = PQconninfo(self->pgconn))) {
|
||||
PyErr_NoMemory();
|
||||
goto exit;
|
||||
}
|
||||
|
||||
res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 0);
|
||||
|
||||
exit:
|
||||
PQconninfoFree(options);
|
||||
|
||||
return res;
|
||||
#else
|
||||
PyErr_SetString(NotSupportedError, "PQconninfo not available in libpq < 9.3");
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* lobject method - allocate a new lobject */
|
||||
|
||||
|
@ -977,6 +1008,8 @@ static struct PyMethodDef connectionObject_methods[] = {
|
|||
METH_NOARGS, psyco_conn_get_transaction_status_doc},
|
||||
{"get_parameter_status", (PyCFunction)psyco_conn_get_parameter_status,
|
||||
METH_VARARGS, psyco_conn_get_parameter_status_doc},
|
||||
{"get_dsn_parameters", (PyCFunction)psyco_conn_get_dsn_parameters,
|
||||
METH_NOARGS, psyco_conn_get_dsn_parameters_doc},
|
||||
{"get_backend_pid", (PyCFunction)psyco_conn_get_backend_pid,
|
||||
METH_NOARGS, psyco_conn_get_backend_pid_doc},
|
||||
{"lobject", (PyCFunction)psyco_conn_lobject,
|
||||
|
@ -1171,7 +1204,7 @@ connection_repr(connectionObject *self)
|
|||
{
|
||||
return PyString_FromFormat(
|
||||
"<connection object at %p; dsn: '%s', closed: %ld>",
|
||||
self, self->dsn, self->closed);
|
||||
self, (self->dsn ? self->dsn : "<unintialized>"), self->closed);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -335,7 +335,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
|
|||
PyErr_Fetch(&err, &arg, &trace);
|
||||
|
||||
if (err && PyErr_GivenExceptionMatches(err, PyExc_TypeError)) {
|
||||
Dprintf("psyco_curs_execute: TypeError exception catched");
|
||||
Dprintf("psyco_curs_execute: TypeError exception caught");
|
||||
PyErr_NormalizeException(&err, &arg, &trace);
|
||||
|
||||
if (PyObject_HasAttrString(arg, "args")) {
|
||||
|
|
|
@ -60,8 +60,8 @@ RAISES_NEG HIDDEN int lobject_export(lobjectObject *self, const char *filename);
|
|||
RAISES_NEG HIDDEN Py_ssize_t lobject_read(lobjectObject *self, char *buf, size_t len);
|
||||
RAISES_NEG HIDDEN Py_ssize_t lobject_write(lobjectObject *self, const char *buf,
|
||||
size_t len);
|
||||
RAISES_NEG HIDDEN long lobject_seek(lobjectObject *self, long pos, int whence);
|
||||
RAISES_NEG HIDDEN long lobject_tell(lobjectObject *self);
|
||||
RAISES_NEG HIDDEN Py_ssize_t lobject_seek(lobjectObject *self, Py_ssize_t pos, int whence);
|
||||
RAISES_NEG HIDDEN Py_ssize_t lobject_tell(lobjectObject *self);
|
||||
RAISES_NEG HIDDEN int lobject_truncate(lobjectObject *self, size_t len);
|
||||
RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
|
||||
|
||||
|
|
|
@ -376,12 +376,12 @@ lobject_read(lobjectObject *self, char *buf, size_t len)
|
|||
|
||||
/* lobject_seek - move the current position in the lo */
|
||||
|
||||
RAISES_NEG long
|
||||
lobject_seek(lobjectObject *self, long pos, int whence)
|
||||
RAISES_NEG Py_ssize_t
|
||||
lobject_seek(lobjectObject *self, Py_ssize_t pos, int whence)
|
||||
{
|
||||
PGresult *pgres = NULL;
|
||||
char *error = NULL;
|
||||
long where;
|
||||
Py_ssize_t where;
|
||||
|
||||
Dprintf("lobject_seek: fd = %d, pos = %ld, whence = %d",
|
||||
self->fd, pos, whence);
|
||||
|
@ -391,12 +391,12 @@ lobject_seek(lobjectObject *self, long pos, int whence)
|
|||
|
||||
#ifdef HAVE_LO64
|
||||
if (self->conn->server_version < 90300) {
|
||||
where = (long)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
|
||||
where = (Py_ssize_t)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
|
||||
} else {
|
||||
where = lo_lseek64(self->conn->pgconn, self->fd, pos, whence);
|
||||
where = (Py_ssize_t)lo_lseek64(self->conn->pgconn, self->fd, pos, whence);
|
||||
}
|
||||
#else
|
||||
where = (long)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
|
||||
where = (Py_ssize_t)lo_lseek(self->conn->pgconn, self->fd, (int)pos, whence);
|
||||
#endif
|
||||
Dprintf("lobject_seek: where = %ld", where);
|
||||
if (where < 0)
|
||||
|
@ -412,12 +412,12 @@ lobject_seek(lobjectObject *self, long pos, int whence)
|
|||
|
||||
/* lobject_tell - tell the current position in the lo */
|
||||
|
||||
RAISES_NEG long
|
||||
RAISES_NEG Py_ssize_t
|
||||
lobject_tell(lobjectObject *self)
|
||||
{
|
||||
PGresult *pgres = NULL;
|
||||
char *error = NULL;
|
||||
long where;
|
||||
Py_ssize_t where;
|
||||
|
||||
Dprintf("lobject_tell: fd = %d", self->fd);
|
||||
|
||||
|
@ -426,12 +426,12 @@ lobject_tell(lobjectObject *self)
|
|||
|
||||
#ifdef HAVE_LO64
|
||||
if (self->conn->server_version < 90300) {
|
||||
where = (long)lo_tell(self->conn->pgconn, self->fd);
|
||||
where = (Py_ssize_t)lo_tell(self->conn->pgconn, self->fd);
|
||||
} else {
|
||||
where = lo_tell64(self->conn->pgconn, self->fd);
|
||||
where = (Py_ssize_t)lo_tell64(self->conn->pgconn, self->fd);
|
||||
}
|
||||
#else
|
||||
where = (long)lo_tell(self->conn->pgconn, self->fd);
|
||||
where = (Py_ssize_t)lo_tell(self->conn->pgconn, self->fd);
|
||||
#endif
|
||||
Dprintf("lobject_tell: where = %ld", where);
|
||||
if (where < 0)
|
||||
|
|
|
@ -105,7 +105,7 @@ psyco_lobj_write(lobjectObject *self, PyObject *args)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
rv = PyInt_FromLong((long)res);
|
||||
rv = PyInt_FromSsize_t((Py_ssize_t)res);
|
||||
|
||||
exit:
|
||||
Py_XDECREF(data);
|
||||
|
@ -121,7 +121,7 @@ static PyObject *
|
|||
psyco_lobj_read(lobjectObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *res;
|
||||
long where, end;
|
||||
Py_ssize_t where, end;
|
||||
Py_ssize_t size = -1;
|
||||
char *buffer;
|
||||
|
||||
|
@ -165,10 +165,10 @@ psyco_lobj_read(lobjectObject *self, PyObject *args)
|
|||
static PyObject *
|
||||
psyco_lobj_seek(lobjectObject *self, PyObject *args)
|
||||
{
|
||||
long offset, pos=0;
|
||||
Py_ssize_t offset, pos=0;
|
||||
int whence=0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "l|i", &offset, &whence))
|
||||
if (!PyArg_ParseTuple(args, "n|i", &offset, &whence))
|
||||
return NULL;
|
||||
|
||||
EXC_IF_LOBJ_CLOSED(self);
|
||||
|
@ -187,8 +187,8 @@ psyco_lobj_seek(lobjectObject *self, PyObject *args)
|
|||
#else
|
||||
if (offset < INT_MIN || offset > INT_MAX) {
|
||||
PyErr_Format(InterfaceError,
|
||||
"offset out of range (%ld): this psycopg version was not built "
|
||||
"with lobject 64 API support",
|
||||
"offset out of range (" FORMAT_CODE_PY_SSIZE_T "): "
|
||||
"this psycopg version was not built with lobject 64 API support",
|
||||
offset);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -197,7 +197,7 @@ psyco_lobj_seek(lobjectObject *self, PyObject *args)
|
|||
if ((pos = lobject_seek(self, offset, whence)) < 0)
|
||||
return NULL;
|
||||
|
||||
return PyLong_FromLong(pos);
|
||||
return PyInt_FromSsize_t(pos);
|
||||
}
|
||||
|
||||
/* tell method - tell current position in the lobject */
|
||||
|
@ -208,7 +208,7 @@ psyco_lobj_seek(lobjectObject *self, PyObject *args)
|
|||
static PyObject *
|
||||
psyco_lobj_tell(lobjectObject *self, PyObject *args)
|
||||
{
|
||||
long pos;
|
||||
Py_ssize_t pos;
|
||||
|
||||
EXC_IF_LOBJ_CLOSED(self);
|
||||
EXC_IF_LOBJ_LEVEL0(self);
|
||||
|
@ -217,7 +217,7 @@ psyco_lobj_tell(lobjectObject *self, PyObject *args)
|
|||
if ((pos = lobject_tell(self)) < 0)
|
||||
return NULL;
|
||||
|
||||
return PyLong_FromLong(pos);
|
||||
return PyInt_FromSsize_t(pos);
|
||||
}
|
||||
|
||||
/* unlink method - unlink (destroy) the lobject */
|
||||
|
@ -274,9 +274,9 @@ psyco_lobj_get_closed(lobjectObject *self, void *closure)
|
|||
static PyObject *
|
||||
psyco_lobj_truncate(lobjectObject *self, PyObject *args)
|
||||
{
|
||||
long len = 0;
|
||||
Py_ssize_t len = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "|l", &len))
|
||||
if (!PyArg_ParseTuple(args, "|n", &len))
|
||||
return NULL;
|
||||
|
||||
EXC_IF_LOBJ_CLOSED(self);
|
||||
|
@ -286,16 +286,16 @@ psyco_lobj_truncate(lobjectObject *self, PyObject *args)
|
|||
#ifdef HAVE_LO64
|
||||
if (len > INT_MAX && self->conn->server_version < 90300) {
|
||||
PyErr_Format(NotSupportedError,
|
||||
"len out of range (%ld): server version %d "
|
||||
"does not support the lobject 64 API",
|
||||
"len out of range (" FORMAT_CODE_PY_SSIZE_T "): "
|
||||
"server version %d does not support the lobject 64 API",
|
||||
len, self->conn->server_version);
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
if (len > INT_MAX) {
|
||||
PyErr_Format(InterfaceError,
|
||||
"len out of range (%ld): this psycopg version was not built "
|
||||
"with lobject 64 API support",
|
||||
"len out of range (" FORMAT_CODE_PY_SSIZE_T "): "
|
||||
"this psycopg version was not built with lobject 64 API support",
|
||||
len);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -170,11 +170,11 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult **pgres)
|
|||
|
||||
if (conn == NULL) {
|
||||
PyErr_SetString(DatabaseError,
|
||||
"psycopg went psycotic and raised a null error");
|
||||
"psycopg went psychotic and raised a null error");
|
||||
return;
|
||||
}
|
||||
|
||||
/* if the connection has somehow beed broken, we mark the connection
|
||||
/* if the connection has somehow been broken, we mark the connection
|
||||
object as closed but requiring cleanup */
|
||||
if (conn->pgconn != NULL && PQstatus(conn->pgconn) == CONNECTION_BAD)
|
||||
conn->closed = 2;
|
||||
|
@ -916,7 +916,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result, int
|
|||
PyErr_SetString(OperationalError, PQerrorMessage(curs->conn->pgconn));
|
||||
return -1;
|
||||
}
|
||||
Dprintf("curs_execute: pg connection at %p OK", curs->conn->pgconn);
|
||||
Dprintf("pq_execute: pg connection at %p OK", curs->conn->pgconn);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
pthread_mutex_lock(&(curs->conn->lock));
|
||||
|
@ -941,7 +941,7 @@ pq_execute(cursorObject *curs, const char *query, int async, int no_result, int
|
|||
Py_UNBLOCK_THREADS;
|
||||
}
|
||||
|
||||
/* dont let pgres = NULL go to pq_fetch() */
|
||||
/* don't let pgres = NULL go to pq_fetch() */
|
||||
if (curs->pgres == NULL) {
|
||||
pthread_mutex_unlock(&(curs->conn->lock));
|
||||
Py_BLOCK_THREADS;
|
||||
|
@ -1409,7 +1409,11 @@ _pq_copy_in_v3(cursorObject *curs)
|
|||
Py_DECREF(str);
|
||||
}
|
||||
}
|
||||
PyErr_Restore(t, ex, tb);
|
||||
/* Clear the Py exception: it will be re-raised from the libpq */
|
||||
Py_XDECREF(t);
|
||||
Py_XDECREF(ex);
|
||||
Py_XDECREF(tb);
|
||||
PyErr_Clear();
|
||||
}
|
||||
res = PQputCopyEnd(curs->conn->pgconn, buf);
|
||||
}
|
||||
|
|
|
@ -132,6 +132,9 @@ STEALS(1) HIDDEN PyObject * psycopg_ensure_bytes(PyObject *obj);
|
|||
|
||||
STEALS(1) HIDDEN PyObject * psycopg_ensure_text(PyObject *obj);
|
||||
|
||||
HIDDEN PyObject *psycopg_dict_from_conninfo_options(PQconninfoOption *options,
|
||||
int include_password);
|
||||
|
||||
/* Exceptions docstrings */
|
||||
#define Error_doc \
|
||||
"Base class for error exceptions."
|
||||
|
|
|
@ -123,8 +123,8 @@ static PyObject *
|
|||
psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
char *err = NULL;
|
||||
PQconninfoOption *options = NULL, *o;
|
||||
PyObject *dict = NULL, *res = NULL, *dsn;
|
||||
PQconninfoOption *options = NULL;
|
||||
PyObject *res = NULL, *dsn;
|
||||
|
||||
static char *kwlist[] = {"dsn", NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) {
|
||||
|
@ -145,26 +145,10 @@ psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (!(dict = PyDict_New())) { goto exit; }
|
||||
for (o = options; o->keyword != NULL; o++) {
|
||||
if (o->val != NULL) {
|
||||
PyObject *value;
|
||||
if (!(value = Text_FromUTF8(o->val))) { goto exit; }
|
||||
if (PyDict_SetItemString(dict, o->keyword, value) != 0) {
|
||||
Py_DECREF(value);
|
||||
goto exit;
|
||||
}
|
||||
Py_DECREF(value);
|
||||
}
|
||||
}
|
||||
|
||||
/* success */
|
||||
res = dict;
|
||||
dict = NULL;
|
||||
res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 1);
|
||||
|
||||
exit:
|
||||
PQconninfoFree(options); /* safe on null */
|
||||
Py_XDECREF(dict);
|
||||
Py_XDECREF(dsn);
|
||||
|
||||
return res;
|
||||
|
|
|
@ -247,3 +247,32 @@ psycopg_is_text_file(PyObject *f)
|
|||
}
|
||||
}
|
||||
|
||||
/* Make a dict out of PQconninfoOption array */
|
||||
PyObject *
|
||||
psycopg_dict_from_conninfo_options(PQconninfoOption *options, int include_password)
|
||||
{
|
||||
PyObject *dict, *res = NULL;
|
||||
PQconninfoOption *o;
|
||||
|
||||
if (!(dict = PyDict_New())) { goto exit; }
|
||||
for (o = options; o->keyword != NULL; o++) {
|
||||
if (o->val != NULL &&
|
||||
(include_password || strcmp(o->keyword, "password") != 0)) {
|
||||
PyObject *value;
|
||||
if (!(value = Text_FromUTF8(o->val))) { goto exit; }
|
||||
if (PyDict_SetItemString(dict, o->keyword, value) != 0) {
|
||||
Py_DECREF(value);
|
||||
goto exit;
|
||||
}
|
||||
Py_DECREF(value);
|
||||
}
|
||||
}
|
||||
|
||||
res = dict;
|
||||
dict = NULL;
|
||||
|
||||
exit:
|
||||
Py_XDECREF(dict);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ def main():
|
|||
file_start = read_base_file(filename)
|
||||
# If you add a version to the list fix the docs (errorcodes.rst, err.rst)
|
||||
classes, errors = fetch_errors(
|
||||
['8.1', '8.2', '8.3', '8.4', '9.0', '9.1', '9.2', '9.3', '9.4'])
|
||||
['8.1', '8.2', '8.3', '8.4', '9.0', '9.1', '9.2', '9.3', '9.4', '9.5'])
|
||||
|
||||
f = open(filename, "w")
|
||||
for line in file_start:
|
||||
|
|
9
setup.py
9
setup.py
|
@ -41,6 +41,7 @@ Programming Language :: Python :: 3.1
|
|||
Programming Language :: Python :: 3.2
|
||||
Programming Language :: Python :: 3.3
|
||||
Programming Language :: Python :: 3.4
|
||||
Programming Language :: Python :: 3.5
|
||||
Programming Language :: C
|
||||
Programming Language :: SQL
|
||||
Topic :: Database
|
||||
|
@ -57,6 +58,9 @@ import os
|
|||
import sys
|
||||
import re
|
||||
import subprocess
|
||||
try:
|
||||
from setuptools import setup, Extension
|
||||
except ImportError:
|
||||
from distutils.core import setup, Extension
|
||||
from distutils.command.build_ext import build_ext
|
||||
from distutils.sysconfig import get_python_inc
|
||||
|
@ -301,6 +305,10 @@ class psycopg_build_ext(build_ext):
|
|||
except AttributeError:
|
||||
ext_path = os.path.join(self.build_lib,
|
||||
'psycopg2', '_psycopg.pyd')
|
||||
# Make sure spawn() will work if compile() was never
|
||||
# called. https://github.com/psycopg/psycopg2/issues/380
|
||||
if not self.compiler.initialized:
|
||||
self.compiler.initialize()
|
||||
self.compiler.spawn(
|
||||
['mt.exe', '-nologo', '-manifest',
|
||||
os.path.join('psycopg', manifest),
|
||||
|
@ -343,6 +351,7 @@ class psycopg_build_ext(build_ext):
|
|||
self.libraries.append("advapi32")
|
||||
if self.compiler_is_msvc():
|
||||
# MSVC requires an explicit "libpq"
|
||||
if "pq" in self.libraries:
|
||||
self.libraries.remove("pq")
|
||||
self.libraries.append("secur32")
|
||||
self.libraries.append("libpq")
|
||||
|
|
|
@ -446,6 +446,13 @@ class MakeDsnTestCase(ConnectingTestCase):
|
|||
self.assertRaises(psycopg2.ProgrammingError,
|
||||
ext.make_dsn, url, nosuch="param")
|
||||
|
||||
@skip_before_libpq(9, 3)
|
||||
def test_get_dsn_parameters(self):
|
||||
conn = self.connect()
|
||||
d = conn.get_dsn_parameters()
|
||||
self.assertEqual(d['dbname'], dbname) # the only param we can check reliably
|
||||
self.assertNotIn('password', d)
|
||||
|
||||
|
||||
class IsolationLevelsTestCase(ConnectingTestCase):
|
||||
|
||||
|
|
|
@ -23,12 +23,14 @@
|
|||
# License for more details.
|
||||
|
||||
import sys
|
||||
from testutils import unittest, ConnectingTestCase, skip_before_libpq
|
||||
import testutils
|
||||
from testutils import unittest, ConnectingTestCase
|
||||
|
||||
import psycopg2
|
||||
import psycopg2.extensions
|
||||
from psycopg2.extensions import b
|
||||
|
||||
|
||||
class QuotingTestCase(ConnectingTestCase):
|
||||
r"""Checks the correct quoting of strings and binary objects.
|
||||
|
||||
|
@ -156,7 +158,7 @@ class QuotingTestCase(ConnectingTestCase):
|
|||
|
||||
|
||||
class TestQuotedString(ConnectingTestCase):
|
||||
def test_encoding(self):
|
||||
def test_encoding_from_conn(self):
|
||||
q = psycopg2.extensions.QuotedString('hi')
|
||||
self.assertEqual(q.encoding, 'latin1')
|
||||
|
||||
|
@ -166,13 +168,13 @@ class TestQuotedString(ConnectingTestCase):
|
|||
|
||||
|
||||
class TestQuotedIdentifier(ConnectingTestCase):
|
||||
@skip_before_libpq(9, 0)
|
||||
@testutils.skip_before_libpq(9, 0)
|
||||
def test_identifier(self):
|
||||
from psycopg2.extensions import quote_ident
|
||||
self.assertEqual(quote_ident('blah-blah', self.conn), '"blah-blah"')
|
||||
self.assertEqual(quote_ident('quote"inside', self.conn), '"quote""inside"')
|
||||
|
||||
@skip_before_libpq(9, 0)
|
||||
@testutils.skip_before_libpq(9, 0)
|
||||
def test_unicode_ident(self):
|
||||
from psycopg2.extensions import quote_ident
|
||||
snowman = u"\u2603"
|
||||
|
@ -183,9 +185,59 @@ class TestQuotedIdentifier(ConnectingTestCase):
|
|||
self.assertEqual(quote_ident(snowman, self.conn), quoted)
|
||||
|
||||
|
||||
class TestStringAdapter(ConnectingTestCase):
|
||||
def test_encoding_default(self):
|
||||
from psycopg2.extensions import adapt
|
||||
a = adapt("hello")
|
||||
self.assertEqual(a.encoding, 'latin1')
|
||||
self.assertEqual(a.getquoted(), b("'hello'"))
|
||||
|
||||
# NOTE: we can't really test an encoding different from utf8, because
|
||||
# when encoding without connection the libpq will use parameters from
|
||||
# a previous one, so what would happens depends jn the tests run order.
|
||||
# egrave = u'\xe8'
|
||||
# self.assertEqual(adapt(egrave).getquoted(), "'\xe8'")
|
||||
|
||||
def test_encoding_error(self):
|
||||
from psycopg2.extensions import adapt
|
||||
snowman = u"\u2603"
|
||||
a = adapt(snowman)
|
||||
self.assertRaises(UnicodeEncodeError, a.getquoted)
|
||||
|
||||
def test_set_encoding(self):
|
||||
# Note: this works-ish mostly in case when the standard db connection
|
||||
# we test with is utf8, otherwise the encoding chosen by PQescapeString
|
||||
# may give bad results.
|
||||
from psycopg2.extensions import adapt
|
||||
snowman = u"\u2603"
|
||||
a = adapt(snowman)
|
||||
a.encoding = 'utf8'
|
||||
self.assertEqual(a.encoding, 'utf8')
|
||||
self.assertEqual(a.getquoted(), b("'\xe2\x98\x83'"))
|
||||
|
||||
def test_connection_wins_anyway(self):
|
||||
from psycopg2.extensions import adapt
|
||||
snowman = u"\u2603"
|
||||
a = adapt(snowman)
|
||||
a.encoding = 'latin9'
|
||||
|
||||
self.conn.set_client_encoding('utf8')
|
||||
a.prepare(self.conn)
|
||||
|
||||
self.assertEqual(a.encoding, 'utf_8')
|
||||
self.assertEqual(a.getquoted(), b("'\xe2\x98\x83'"))
|
||||
|
||||
@testutils.skip_before_python(3)
|
||||
def test_adapt_bytes(self):
|
||||
snowman = u"\u2603"
|
||||
self.conn.set_client_encoding('utf8')
|
||||
a = psycopg2.extensions.QuotedString(snowman.encode('utf8'))
|
||||
a.prepare(self.conn)
|
||||
self.assertEqual(a.getquoted(), b("'\xe2\x98\x83'"))
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
|
|
@ -192,6 +192,7 @@ class TypesBasicTests(ConnectingTestCase):
|
|||
self.assertRaises(psycopg2.DataError,
|
||||
psycopg2.extensions.STRINGARRAY, b(s), curs)
|
||||
|
||||
@testutils.skip_before_postgres(8, 2)
|
||||
def testArrayOfNulls(self):
|
||||
curs = self.conn.cursor()
|
||||
curs.execute("""
|
||||
|
@ -379,7 +380,6 @@ class AdaptSubclassTest(unittest.TestCase):
|
|||
finally:
|
||||
del psycopg2.extensions.adapters[A, psycopg2.extensions.ISQLQuote]
|
||||
|
||||
|
||||
@testutils.skip_before_python(3)
|
||||
def test_adapt_subtype_3(self):
|
||||
from psycopg2.extensions import adapt, register_adapter, AsIs
|
||||
|
@ -479,6 +479,7 @@ class ByteaParserTest(unittest.TestCase):
|
|||
|
||||
self.assertEqual(rv, tgt)
|
||||
|
||||
|
||||
def skip_if_cant_cast(f):
|
||||
@wraps(f)
|
||||
def skip_if_cant_cast_(self, *args, **kwargs):
|
||||
|
@ -498,4 +499,3 @@ def test_suite():
|
|||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class ConnectingTestCase(unittest.TestCase):
|
|||
self._conns
|
||||
except AttributeError, e:
|
||||
raise AttributeError(
|
||||
"%s (did you remember calling ConnectingTestCase.setUp()?)"
|
||||
"%s (did you forget to call ConnectingTestCase.setUp()?)"
|
||||
% e)
|
||||
|
||||
if 'dsn' in kwargs:
|
||||
|
|
Loading…
Reference in New Issue
Block a user