diff options
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/databases/postgres.py | 23 | ||||
| -rw-r--r-- | lib/sqlalchemy/engine/base.py | 5 | ||||
| -rw-r--r-- | lib/sqlalchemy/sql/expression.py | 53 |
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): |
