]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
- use itertools.count() plus mutex for Query _new_runid, psycopg2 server
authorMike Bayer <mike_mp@zzzcomputing.com>
Sat, 6 Aug 2011 18:21:19 +0000 (14:21 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 6 Aug 2011 18:21:19 +0000 (14:21 -0400)
side cursor names, mentinoed in [ticket:2247]

CHANGES
lib/sqlalchemy/dialects/postgresql/psycopg2.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/util/__init__.py
lib/sqlalchemy/util/langhelpers.py

diff --git a/CHANGES b/CHANGES
index 9960e72dea62aa7021908427e3d822acc8af86f1..d0d599b4c109a270b4bb53e3aad217973539b5a9 100644 (file)
--- 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
index 2a3b4297c3b559e5f9c543ec3a60e8eef8c9b6d6..22487a3590cca17b6b3d604bdc3014ad42f3630c 100644 (file)
@@ -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()
index 7f1d08f2f4155ce2e417b5a38883bca9a792e299..cf907b8795a4d0c0a59e3ba1ed1d2be6775464ca 100644 (file)
@@ -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()
index 4b7a43752adaa99ee1b621aa1ffbf60b4baa2c5d..c78cddf778924f2c3e57fe12a1abef1fc2a37672 100644 (file)
@@ -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
index c3a358220041d4e8b6382bb678ce390b7fd2fa19..82f28f8ec152d376e190212403958736a0d88d8a 100644 (file)
@@ -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__