From: Mike Bayer Date: Sat, 6 Aug 2011 18:21:19 +0000 (-0400) Subject: - use itertools.count() plus mutex for Query _new_runid, psycopg2 server X-Git-Tag: rel_0_7_3~97 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c13dee04139837bdd6ff5ff70ebeca09692ee384;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - use itertools.count() plus mutex for Query _new_runid, psycopg2 server side cursor names, mentinoed in [ticket:2247] --- diff --git a/CHANGES b/CHANGES index 9960e72dea..d0d599b4c1 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,11 @@ CHANGES the database regardless of whether C extensions are in use or not. +- postgresql + - Use an atomic counter as the "random number" + source for server side cursor names; + conflicts have been reported in rare cases. + - ext - Added local_attr, remote_attr, attr accessors to AssociationProxy, providing quick access diff --git a/lib/sqlalchemy/dialects/postgresql/psycopg2.py b/lib/sqlalchemy/dialects/postgresql/psycopg2.py index 2a3b4297c3..22487a3590 100644 --- a/lib/sqlalchemy/dialects/postgresql/psycopg2.py +++ b/lib/sqlalchemy/dialects/postgresql/psycopg2.py @@ -97,7 +97,6 @@ The psycopg2 dialect will log Postgresql NOTICE messages via the """ -import random import re import logging @@ -165,6 +164,8 @@ SERVER_SIDE_CURSOR_RE = re.compile( r'\s*SELECT', re.I | re.UNICODE) +_server_side_id = util.counter() + class PGExecutionContext_psycopg2(PGExecutionContext): def create_cursor(self): # TODO: coverage for server side cursors + select.for_update() @@ -187,7 +188,7 @@ class PGExecutionContext_psycopg2(PGExecutionContext): if is_server_side: # use server-side cursors: # http://lists.initd.org/pipermail/psycopg/2007-January/005251.html - ident = "c_%s_%s" % (hex(id(self))[2:], hex(random.randint(0, 65535))[2:]) + ident = "c_%s_%s" % (hex(id(self))[2:], hex(_server_side_id())[2:]) return self._dbapi_connection.cursor(ident) else: return self._dbapi_connection.cursor() diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index 7f1d08f2f4..cf907b8795 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -3161,14 +3161,4 @@ class AliasOption(interfaces.MapperOption): query._from_obj_alias = sql_util.ColumnAdapter(alias) -_runid = 1L -_id_lock = util.threading.Lock() - -def _new_runid(): - global _runid - _id_lock.acquire() - try: - _runid += 1 - return _runid - finally: - _id_lock.release() +_new_runid = util.counter() diff --git a/lib/sqlalchemy/util/__init__.py b/lib/sqlalchemy/util/__init__.py index 4b7a43752a..c78cddf778 100644 --- a/lib/sqlalchemy/util/__init__.py +++ b/lib/sqlalchemy/util/__init__.py @@ -27,7 +27,7 @@ from langhelpers import iterate_attributes, class_hierarchy, \ duck_type_collection, assert_arg_type, symbol, dictlike_iteritems,\ classproperty, set_creation_order, warn_exception, warn, NoneType,\ constructor_copy, methods_equivalent, chop_traceback, asint,\ - generic_repr + generic_repr, counter from deprecations import warn_deprecated, warn_pending_deprecation, \ deprecated, pending_deprecation diff --git a/lib/sqlalchemy/util/langhelpers.py b/lib/sqlalchemy/util/langhelpers.py index c3a3582200..82f28f8ec1 100644 --- a/lib/sqlalchemy/util/langhelpers.py +++ b/lib/sqlalchemy/util/langhelpers.py @@ -610,6 +610,21 @@ def constructor_copy(obj, cls, **kw): return cls(**kw) +def counter(): + """Return a threadsafe counter function.""" + + lock = threading.Lock() + counter = itertools.count(1L) + + def next(): + lock.acquire() + try: + return counter.next() + finally: + lock.release() + + return next + def duck_type_collection(specimen, default=None): """Given an instance or class, guess if it is or is acting as one of the basic collection types: list, set and dict. If the __emulates__