mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 17:06:33 +03:00
Merge branch 'fix-829'
This commit is contained in:
commit
e569e49b8b
13
NEWS
13
NEWS
|
@ -24,8 +24,6 @@ New features:
|
||||||
(:ticket:`#732`).
|
(:ticket:`#732`).
|
||||||
- Added *fetch* parameter to `~psycopg2.extras.execute_values()` function
|
- Added *fetch* parameter to `~psycopg2.extras.execute_values()` function
|
||||||
(:ticket:`#813`).
|
(:ticket:`#813`).
|
||||||
- Fixed adaptation of numeric subclasses such as `~enum.IntEnum`
|
|
||||||
(:ticket:`#591`).
|
|
||||||
- `!str()` on `~psycopg2.extras.Range` produces a human-readable representation
|
- `!str()` on `~psycopg2.extras.Range` produces a human-readable representation
|
||||||
(:ticket:`#773`).
|
(:ticket:`#773`).
|
||||||
- `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows
|
- `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows
|
||||||
|
@ -33,8 +31,15 @@ New features:
|
||||||
- Added `~psycopg2.extensions.Diagnostics.severity_nonlocalized` attribute on
|
- Added `~psycopg2.extensions.Diagnostics.severity_nonlocalized` attribute on
|
||||||
the `~psycopg2.extensions.Diagnostics` object (:ticket:`#783`).
|
the `~psycopg2.extensions.Diagnostics` object (:ticket:`#783`).
|
||||||
- More efficient `~psycopg2.extras.NamedTupleCursor` (:ticket:`#838`).
|
- More efficient `~psycopg2.extras.NamedTupleCursor` (:ticket:`#838`).
|
||||||
- Async communication improved to fix blocking on results returned at
|
|
||||||
different times (:ticket:`#856`).
|
Bug fixes:
|
||||||
|
|
||||||
|
- Fixed connections occasionally broken by the unrelated use of the
|
||||||
|
multiprocessing module (:ticket:`#829`).
|
||||||
|
- Fixed async communication blocking if results are returned in different
|
||||||
|
chunks, e.g. with notices interspersed to the results (:ticket:`#856`).
|
||||||
|
- Fixed adaptation of numeric subclasses such as `~enum.IntEnum`
|
||||||
|
(:ticket:`#591`).
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,18 @@
|
||||||
# define HIDDEN
|
# define HIDDEN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* support for getpid() */
|
||||||
|
#if defined( __GNUC__)
|
||||||
|
#define CONN_CHECK_PID
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Windows doesn't seem affected by bug #829: just make it compile. */
|
||||||
|
#define pid_t int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* debug printf-like function */
|
/* debug printf-like function */
|
||||||
#ifdef PSYCOPG_DEBUG
|
#ifdef PSYCOPG_DEBUG
|
||||||
extern HIDDEN int psycopg_debug_enabled;
|
extern HIDDEN int psycopg_debug_enabled;
|
||||||
|
@ -40,8 +52,6 @@ extern HIDDEN int psycopg_debug_enabled;
|
||||||
|
|
||||||
#if defined( __GNUC__) && !defined(__APPLE__)
|
#if defined( __GNUC__) && !defined(__APPLE__)
|
||||||
#ifdef PSYCOPG_DEBUG
|
#ifdef PSYCOPG_DEBUG
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#define Dprintf(fmt, args...) \
|
#define Dprintf(fmt, args...) \
|
||||||
if (!psycopg_debug_enabled) ; else \
|
if (!psycopg_debug_enabled) ; else \
|
||||||
fprintf(stderr, "[%d] " fmt "\n", (int) getpid() , ## args)
|
fprintf(stderr, "[%d] " fmt "\n", (int) getpid() , ## args)
|
||||||
|
|
|
@ -141,6 +141,9 @@ struct connectionObject {
|
||||||
int isolevel;
|
int isolevel;
|
||||||
int readonly;
|
int readonly;
|
||||||
int deferrable;
|
int deferrable;
|
||||||
|
|
||||||
|
/* the pid this connection was created into */
|
||||||
|
pid_t procpid;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* map isolation level values into a numeric const */
|
/* map isolation level values into a numeric const */
|
||||||
|
|
|
@ -1367,6 +1367,10 @@ connection_setup(connectionObject *self, const char *dsn, long int async)
|
||||||
self->isolevel = ISOLATION_LEVEL_DEFAULT;
|
self->isolevel = ISOLATION_LEVEL_DEFAULT;
|
||||||
self->readonly = STATE_DEFAULT;
|
self->readonly = STATE_DEFAULT;
|
||||||
self->deferrable = STATE_DEFAULT;
|
self->deferrable = STATE_DEFAULT;
|
||||||
|
#ifdef CONN_CHECK_PID
|
||||||
|
self->procpid = getpid();
|
||||||
|
#endif
|
||||||
|
|
||||||
/* other fields have been zeroed by tp_alloc */
|
/* other fields have been zeroed by tp_alloc */
|
||||||
|
|
||||||
pthread_mutex_init(&(self->lock), NULL);
|
pthread_mutex_init(&(self->lock), NULL);
|
||||||
|
@ -1420,7 +1424,15 @@ connection_dealloc(PyObject* obj)
|
||||||
* resulting in a double-free segfault (ticket #166). */
|
* resulting in a double-free segfault (ticket #166). */
|
||||||
PyObject_GC_UnTrack(self);
|
PyObject_GC_UnTrack(self);
|
||||||
|
|
||||||
conn_close(self);
|
/* close the connection only if this is the same process it was created
|
||||||
|
* into, otherwise using multiprocessing we may close the connection
|
||||||
|
* belonging to another process. */
|
||||||
|
#ifdef CONN_CHECK_PID
|
||||||
|
if (self->procpid == getpid())
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
conn_close(self);
|
||||||
|
}
|
||||||
|
|
||||||
if (self->weakreflist) {
|
if (self->weakreflist) {
|
||||||
PyObject_ClearWeakRefs(obj);
|
PyObject_ClearWeakRefs(obj);
|
||||||
|
|
|
@ -22,14 +22,16 @@
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
# License for more details.
|
# License for more details.
|
||||||
|
|
||||||
import ctypes
|
|
||||||
import gc
|
import gc
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess as sp
|
|
||||||
import sys
|
import sys
|
||||||
import threading
|
|
||||||
import time
|
import time
|
||||||
|
import ctypes
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
import threading
|
||||||
|
import subprocess as sp
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from weakref import ref
|
from weakref import ref
|
||||||
|
@ -360,6 +362,47 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
conn.close()
|
conn.close()
|
||||||
self.assert_(conn.pgconn_ptr is None)
|
self.assert_(conn.pgconn_ptr is None)
|
||||||
|
|
||||||
|
@slow
|
||||||
|
def test_multiprocess_close(self):
|
||||||
|
dir = tempfile.mkdtemp()
|
||||||
|
try:
|
||||||
|
with open(os.path.join(dir, "mptest.py"), 'w') as f:
|
||||||
|
f.write("""\
|
||||||
|
import time
|
||||||
|
import psycopg2
|
||||||
|
|
||||||
|
def thread():
|
||||||
|
conn = psycopg2.connect(%(dsn)r)
|
||||||
|
curs = conn.cursor()
|
||||||
|
for i in range(10):
|
||||||
|
curs.execute("select 1")
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def process():
|
||||||
|
time.sleep(0.2)
|
||||||
|
""" % {'dsn': dsn})
|
||||||
|
|
||||||
|
script = ("""\
|
||||||
|
import sys
|
||||||
|
sys.path.insert(0, %(dir)r)
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
import multiprocessing
|
||||||
|
import mptest
|
||||||
|
|
||||||
|
t = threading.Thread(target=mptest.thread, name='mythread')
|
||||||
|
t.start()
|
||||||
|
time.sleep(0.2)
|
||||||
|
multiprocessing.Process(target=mptest.process, name='myprocess').start()
|
||||||
|
t.join()
|
||||||
|
""" % {'dir': dir})
|
||||||
|
|
||||||
|
out = sp.check_output(
|
||||||
|
[sys.executable, '-c', script], stderr=sp.STDOUT)
|
||||||
|
self.assertEqual(out, b'', out)
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(dir, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
class ParseDsnTestCase(ConnectingTestCase):
|
class ParseDsnTestCase(ConnectingTestCase):
|
||||||
def test_parse_dsn(self):
|
def test_parse_dsn(self):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user