summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/databases/postgres.py23
-rw-r--r--lib/sqlalchemy/engine/base.py5
-rw-r--r--lib/sqlalchemy/sql/expression.py53
3 files changed, 47 insertions, 34 deletions
diff --git a/lib/sqlalchemy/databases/postgres.py b/lib/sqlalchemy/databases/postgres.py
index 94ad7d2e4..326dd6b7d 100644
--- a/lib/sqlalchemy/databases/postgres.py
+++ b/lib/sqlalchemy/databases/postgres.py
@@ -224,6 +224,10 @@ def descriptor():
('host',"Hostname", None),
]}
+SERVER_SIDE_CURSOR_RE = re.compile(
+ r'\s*SELECT',
+ re.I | re.UNICODE)
+
SELECT_RE = re.compile(
r'\s*(?:SELECT|FETCH|(UPDATE|INSERT))',
re.I | re.UNICODE)
@@ -252,7 +256,6 @@ RETURNING_QUOTED_RE = re.compile(
\sRETURNING\s""", re.I | re.UNICODE | re.VERBOSE)
class PGExecutionContext(default.DefaultExecutionContext):
-
def returns_rows_text(self, statement):
m = SELECT_RE.match(statement)
return m and (not m.group(1) or (RETURNING_RE.search(statement)
@@ -265,23 +268,20 @@ class PGExecutionContext(default.DefaultExecutionContext):
)
def create_cursor(self):
- # executing a default or Sequence standalone creates an execution context without a statement.
- # so slightly hacky "if no statement assume we're server side" logic
- # TODO: dont use regexp if Compiled is used ?
self.__is_server_side = \
self.dialect.server_side_cursors and \
- (self.statement is None or \
- (SELECT_RE.match(self.statement) and not re.search(r'FOR UPDATE(?: NOWAIT)?\s*$', self.statement, re.I))
- )
+ ((self.compiled and isinstance(self.compiled.statement, expression.Selectable)) \
+ or \
+ (not self.compiled and self.statement and SERVER_SIDE_CURSOR_RE.match(self.statement)))
if self.__is_server_side:
# use server-side cursors:
# http://lists.initd.org/pipermail/psycopg/2007-January/005251.html
- ident = "c" + hex(random.randint(0, 65535))[2:]
+ ident = "c_%s_%s" % (hex(id(self))[2:], hex(random.randint(0, 65535))[2:])
return self._connection.connection.cursor(ident)
else:
return self._connection.connection.cursor()
-
+
def get_result_proxy(self):
if self.__is_server_side:
return base.BufferedRowResultProxy(self)
@@ -763,6 +763,11 @@ class PGSchemaDropper(compiler.SchemaDropper):
self.execute()
class PGDefaultRunner(base.DefaultRunner):
+ def __init__(self, context):
+ base.DefaultRunner.__init__(self, context)
+ # craete cursor which won't conflict with a server-side cursor
+ self.cursor = context._connection.connection.cursor()
+
def get_column_default(self, column, isinsert=True):
if column.primary_key:
# pre-execute passive defaults on primary keys
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index cd662ac92..722acd6f0 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -1806,6 +1806,7 @@ class DefaultRunner(schema.SchemaVisitor):
def __init__(self, context):
self.context = context
self.dialect = context.dialect
+ self.cursor = context.cursor
def get_column_default(self, column):
if column.default is not None:
@@ -1846,8 +1847,8 @@ class DefaultRunner(schema.SchemaVisitor):
conn = self.context._connection
if isinstance(stmt, unicode) and not self.dialect.supports_unicode_statements:
stmt = stmt.encode(self.dialect.encoding)
- conn._cursor_execute(self.context.cursor, stmt, params)
- return self.context.cursor.fetchone()[0]
+ conn._cursor_execute(self.cursor, stmt, params)
+ return self.cursor.fetchone()[0]
def visit_column_onupdate(self, onupdate):
if isinstance(onupdate.arg, expression.ClauseElement):
diff --git a/lib/sqlalchemy/sql/expression.py b/lib/sqlalchemy/sql/expression.py
index d8a85d8bd..c487ee173 100644
--- a/lib/sqlalchemy/sql/expression.py
+++ b/lib/sqlalchemy/sql/expression.py
@@ -3496,7 +3496,35 @@ class _UpdateBase(ClauseElement):
self._bind = bind
bind = property(bind, _set_bind)
-class Insert(_UpdateBase):
+class _ValuesBase(_UpdateBase):
+ def values(self, *args, **kwargs):
+ """specify the VALUES clause for an INSERT statement, or the SET clause for an UPDATE.
+
+ \**kwargs
+ key=<somevalue> arguments
+
+ \*args
+ deprecated. A single dictionary can be sent as the first positional argument.
+ """
+
+ if args:
+ v = args[0]
+ else:
+ v = {}
+ if len(v) == 0 and len(kwargs) == 0:
+ return self
+ u = self._clone()
+
+ if u.parameters is None:
+ u.parameters = u._process_colparams(v)
+ u.parameters.update(kwargs)
+ else:
+ u.parameters = self.parameters.copy()
+ u.parameters.update(u._process_colparams(v))
+ u.parameters.update(kwargs)
+ return u
+
+class Insert(_ValuesBase):
def __init__(self, table, values=None, inline=False, bind=None, prefixes=None, **kwargs):
self._bind = bind
self.table = table
@@ -3520,17 +3548,6 @@ class Insert(_UpdateBase):
def _copy_internals(self, clone=_clone):
self.parameters = self.parameters.copy()
- def values(self, v):
- if len(v) == 0:
- return self
- u = self._clone()
- if u.parameters is None:
- u.parameters = u._process_colparams(v)
- else:
- u.parameters = self.parameters.copy()
- u.parameters.update(u._process_colparams(v))
- return u
-
def prefix_with(self, clause):
"""Add a word or expression between INSERT and INTO. Generative.
@@ -3542,7 +3559,7 @@ class Insert(_UpdateBase):
gen._prefixes = self._prefixes + [clause]
return gen
-class Update(_UpdateBase):
+class Update(_ValuesBase):
def __init__(self, table, whereclause, values=None, inline=False, bind=None, **kwargs):
self._bind = bind
self.table = table
@@ -3576,16 +3593,6 @@ class Update(_UpdateBase):
s._whereclause = _literal_as_text(whereclause)
return s
- def values(self, v):
- if len(v) == 0:
- return self
- u = self._clone()
- if u.parameters is None:
- u.parameters = u._process_colparams(v)
- else:
- u.parameters = self.parameters.copy()
- u.parameters.update(u._process_colparams(v))
- return u
class Delete(_UpdateBase):
def __init__(self, table, whereclause, bind=None):