def checkout(self): if self.connection is None: raise exc.InvalidRequestError("This connection is closed") self.__counter += 1 if not self._pool.dispatch.checkout or self.__counter != 1: return self # Pool listeners can trigger a reconnection on checkout attempts = 2 while attempts > 0: try: self._pool.dispatch.checkout(self.connection, self._connection_record, self) return self except exc.DisconnectionError as e: self._pool.logger.info( "Disconnection detected on checkout: %s", e) self._connection_record.invalidate(e) self.connection = self._connection_record.get_connection() attempts -= 1 self._pool.logger.info("Reconnection attempts exhausted on checkout") self.invalidate() raise exc.InvalidRequestError("This connection is closed")
def pre_exec(self): if self.isinsert: tbl = self.compiled.statement.table seq_column = tbl._autoincrement_column insert_has_sequence = seq_column is not None if insert_has_sequence: self._enable_identity_insert = \ seq_column.key in self.compiled_parameters[0] else: self._enable_identity_insert = False if self._enable_identity_insert: self.cursor.execute( "SET IDENTITY_INSERT %s ON" % self.dialect.identifier_preparer.format_table(tbl)) if self.isddl: # TODO: to enhance this, we can detect "ddl in tran" on the # database settings. this error message should be improved to # include a note about that. if not self.should_autocommit: raise exc.InvalidRequestError( "The Sybase dialect only supports " "DDL in 'autocommit' mode at this time.") self.root_connection.engine.logger.info( "AUTOCOMMIT (Assuming no Sybase 'ddl in tran')") self.set_ddl_autocommit(self.root_connection.connection.connection, True)
def contains(self, obj): """Produce a proxied 'contains' expression using EXISTS. This expression will be a composed product using the :meth:`.RelationshipProperty.Comparator.any` , :meth:`.RelationshipProperty.Comparator.has`, and/or :meth:`.RelationshipProperty.Comparator.contains` operators of the underlying proxied attributes. """ target_assoc = self._unwrap_target_assoc_proxy if target_assoc is not None: return self._comparator._criterion_exists( target_assoc.contains(obj) if not target_assoc.scalar else target_assoc == obj) elif self._target_is_object and self.scalar and \ not self._value_is_scalar: return self._comparator.has( getattr(self.target_class, self.value_attr).contains(obj)) elif self._target_is_object and self.scalar and \ self._value_is_scalar: raise exc.InvalidRequestError( "contains() doesn't apply to a scalar object endpoint; use ==") else: return self._comparator._criterion_exists(**{self.value_attr: obj})
def listen(target, identifier, fn, *args, **kw): """Register a listener function for the given target. e.g.:: from sqlalchemy import event from sqlalchemy.schema import UniqueConstraint def unique_constraint_name(const, table): const.name = "uq_%s_%s" % ( table.name, list(const.columns)[0].name ) event.listen( UniqueConstraint, "after_parent_attach", unique_constraint_name) """ for evt_cls in _registrars[identifier]: tgt = evt_cls._accept_with(target) if tgt is not None: tgt.dispatch._listen(tgt, identifier, fn, *args, **kw) return raise exc.InvalidRequestError("No such event '%s' for target '%s'" % (identifier, target))
def visit_select(self, select, **kwargs): """Look for ``LIMIT`` and OFFSET in a select statement, and if so tries to wrap it in a subquery with ``row_number()`` criterion. """ if not getattr(select, '_mssql_visit', None) and select._offset: # to use ROW_NUMBER(), an ORDER BY is required. orderby = self.process(select._order_by_clause) if not orderby: raise exc.InvalidRequestError( 'MSSQL requires an order_by when ' 'using an offset.') _offset = select._offset _limit = select._limit select._mssql_visit = True select = select.column( sql.literal_column("ROW_NUMBER() OVER (ORDER BY %s)" \ % orderby).label("mssql_rn") ).order_by(None).alias() limitselect = sql.select( [c for c in select.c if c.key != 'mssql_rn']) limitselect.append_whereclause("mssql_rn>%d" % _offset) if _limit is not None: limitselect.append_whereclause("mssql_rn<=%d" % (_limit + _offset)) return self.process(limitselect, iswrapper=True, **kwargs) else: return compiler.SQLCompiler.visit_select(self, select, **kwargs)
def __init__(self, auto_convert_lobs=True, threaded=True, coerce_to_unicode=True, coerce_to_decimal=True, arraysize=50, **kwargs): self._pop_deprecated_kwargs(kwargs) OracleDialect.__init__(self, **kwargs) self.threaded = threaded self.arraysize = arraysize self.auto_convert_lobs = auto_convert_lobs self.coerce_to_unicode = coerce_to_unicode self.coerce_to_decimal = coerce_to_decimal if self._use_nchar_for_unicode: self.colspecs = self.colspecs.copy() self.colspecs[sqltypes.Unicode] = _OracleUnicodeStringNCHAR self.colspecs[sqltypes.UnicodeText] = _OracleUnicodeTextNCLOB cx_Oracle = self.dbapi if cx_Oracle is None: self._include_setinputsizes = {} self.cx_oracle_ver = (0, 0, 0) else: self.cx_oracle_ver = self._parse_cx_oracle_ver(cx_Oracle.version) if self.cx_oracle_ver < (5, 2) and self.cx_oracle_ver > (0, 0, 0): raise exc.InvalidRequestError( "cx_Oracle version 5.2 and above are supported") self._has_native_int = hasattr(cx_Oracle, "NATIVE_INT") self._include_setinputsizes = { cx_Oracle.NCLOB, cx_Oracle.CLOB, cx_Oracle.LOB, cx_Oracle.NCHAR, cx_Oracle.FIXED_NCHAR, cx_Oracle.BLOB, cx_Oracle.FIXED_CHAR, cx_Oracle.TIMESTAMP, _OracleInteger, _OracleBINARY_FLOAT, _OracleBINARY_DOUBLE } self._paramval = lambda value: value.getvalue() # https://github.com/oracle/python-cx_Oracle/issues/176#issuecomment-386821291 # https://github.com/oracle/python-cx_Oracle/issues/224 self._values_are_lists = self.cx_oracle_ver >= (6, 3) if self._values_are_lists: cx_Oracle.__future__.dml_ret_array_val = True def _returningval(value): try: return value.values[0][0] except IndexError: return None self._returningval = _returningval else: self._returningval = self._paramval self._is_cx_oracle_6 = self.cx_oracle_ver >= (6, )
def result_processor(self, dialect, coltype): if dialect.datetimeformat == 'internal': def process(value): if value is None: return None else: return datetime.datetime(*[ int(v) for v in (value[0:4], value[4:6], value[6:8], value[8:10], value[10:12], value[12:14], value[14:]) ]) elif dialect.datetimeformat == 'iso': def process(value): if value is None: return None else: return datetime.datetime(*[ int(v) for v in (value[0:4], value[5:7], value[8:10], value[11:13], value[14:16], value[17:19], value[20:]) ]) else: raise exc.InvalidRequestError( "datetimeformat '%s' is not supported." % dialect.datetimeformat) return process
def get_column_specification(self, column, **kwargs): colspec = (self.preparer.format_column(column) + " " + self.dialect.type_compiler.process(column.type)) if column.nullable is not None: if not column.nullable or column.primary_key: colspec += " NOT NULL" else: colspec += " NULL" if column.table is None: raise exc.InvalidRequestError("mssql requires Table-bound columns " "in order to generate DDL") seq_col = column.table._autoincrement_column # install a IDENTITY Sequence if we have an implicit IDENTITY column if seq_col is column: sequence = isinstance(column.default, sa_schema.Sequence) and \ column.default if sequence: start, increment = sequence.start or 1, \ sequence.increment or 1 else: start, increment = 1, 1 colspec += " IDENTITY(%s,%s)" % (start, increment) else: default = self.get_column_default_string(column) if default is not None: colspec += " DEFAULT " + default return colspec
def with_totals(self): if not self._group_by: raise exc.InvalidRequestError( "Query.with_totals() can be used only with specified " "GROUP BY, call group_by()") self._with_totals = True
def create_row_processor(self, context, path, mapper, row, adapter): if not self.parent.class_manager[self.key].impl.supports_population: raise sa_exc.InvalidRequestError( "'%s' does not support object " "population - eager loading cannot be applied." % self) path = path[self.key] our_col = path.get(context, "nested_result") if our_col in row: _instance = loading.instance_processor(self.mapper, context, path[self.mapper], None) if not self.uselist: return self._create_scalar_loader(context, self.key, our_col, _instance) else: return self._create_collection_loader(context, self.key, our_col, _instance) else: return self.parent_property.\ _get_strategy(LazyLoader).\ create_row_processor( context, path, mapper, row, adapter)
def last_inserted_ids(self): if self.context.last_inserted_ids is None: raise exc.InvalidRequestError( "no INSERT executed, or can't use cursor.lastrowid without Postgres OIDs enabled" ) else: return self.context.last_inserted_ids
def order_by_clause(self, select, **kw): order_by = self.process(select._order_by_clause, **kw) # ORDER BY clauses in DISTINCT queries must reference aliased # inner columns by alias name, not true column name. if order_by and getattr(select, '_distinct', False): labels = self._find_labeled_columns(select.inner_columns, select.use_labels) if labels: for needs_alias in labels.keys(): r = re.compile(r'(^| )(%s)(,| |$)' % re.escape(needs_alias)) order_by = r.sub((r'\1%s\3' % labels[needs_alias]), order_by) # No ORDER BY in subqueries. if order_by: if self.is_subquery(): # It's safe to simply drop the ORDER BY if there is no # LIMIT. Right? Other dialects seem to get away with # dropping order. if select._limit: raise exc.InvalidRequestError( "MaxDB does not support ORDER BY in subqueries") else: return "" return " ORDER BY " + order_by else: return ""
def result_processor(self, dialect, coltype): if self.asdecimal: if coltype in _FLOAT_TYPES: return processors.to_decimal_processor_factory(decimal.Decimal) elif coltype in _DECIMAL_TYPES: # pg8000 returns Decimal natively for 1700 return None else: raise exc.InvalidRequestError("Unknown PG numeric type: %d" % coltype) else: if coltype in _FLOAT_TYPES: # pg8000 returns float natively for 701 return None elif coltype in _DECIMAL_TYPES: return processors.to_float else: raise exc.InvalidRequestError("Unknown PG numeric type: %d" % coltype)
def visit_select_precolumns(self, select): """Access puts TOP, it's version of LIMIT here """ s = select.distinct and "DISTINCT " or "" if select.limit: s += "TOP %s " % (select.limit) if select.offset: raise exc.InvalidRequestError('Access does not support LIMIT with an offset') return s
def create_connect_args(self, url): dialect_opts = dict(url.query) for opt in ( "use_ansi", "auto_setinputsizes", "auto_convert_lobs", "threaded", "allow_twophase", ): if opt in dialect_opts: util.coerce_kw_type(dialect_opts, opt, bool) setattr(self, opt, dialect_opts[opt]) database = url.database service_name = dialect_opts.get("service_name", None) if database or service_name: # if we have a database, then we have a remote host port = url.port if port: port = int(port) else: port = 1521 if database and service_name: raise exc.InvalidRequestError( '"service_name" option shouldn\'t ' 'be used with a "database" part of the url') if database: makedsn_kwargs = {"sid": database} if service_name: makedsn_kwargs = {"service_name": service_name} dsn = self.dbapi.makedsn(url.host, port, **makedsn_kwargs) else: # we have a local tnsname dsn = url.host opts = dict(threaded=self.threaded) if dsn is not None: opts["dsn"] = dsn if url.password is not None: opts["password"] = url.password if url.username is not None: opts["user"] = url.username if "mode" in url.query: opts["mode"] = url.query["mode"] if isinstance(opts["mode"], util.string_types): mode = opts["mode"].upper() if mode == "SYSDBA": opts["mode"] = self.dbapi.SYSDBA elif mode == "SYSOPER": opts["mode"] = self.dbapi.SYSOPER else: util.coerce_kw_type(opts, "mode", int) return ([], opts)
def get_select_precolumns(self, select): # Convert a subquery's LIMIT to TOP sql = select._distinct and 'DISTINCT ' or '' if self.is_subquery() and select._limit: if select._offset: raise exc.InvalidRequestError( 'MaxDB does not support LIMIT with an offset.') sql += 'TOP %s ' % select._limit return sql
def create_connect_args(self, url): dialect_opts = dict(url.query) for opt in ('use_ansi', 'auto_setinputsizes', 'auto_convert_lobs', 'threaded', 'allow_twophase'): if opt in dialect_opts: util.coerce_kw_type(dialect_opts, opt, bool) setattr(self, opt, dialect_opts[opt]) database = url.database service_name = dialect_opts.get('service_name', None) if database.endswith(':timesten_direct') or database.endswith( ':timesten_client'): dsn = url.host + '/' + database self.is_timesten = True elif database or service_name: # if we have a database, then we have a remote host port = url.port if port: port = int(port) else: port = 1521 if database and service_name: raise exc.InvalidRequestError( '"service_name" option shouldn\'t ' 'be used with a "database" part of the url') if database: makedsn_kwargs = {'sid': database} if service_name: makedsn_kwargs = {'service_name': service_name} dsn = self.dbapi.makedsn(url.host, port, **makedsn_kwargs) else: # we have a local tnsname dsn = url.host opts = dict(threaded=self.threaded, ) if dsn is not None: opts['dsn'] = dsn if url.password is not None: opts['password'] = url.password if url.username is not None: opts['user'] = url.username if 'mode' in url.query: opts['mode'] = url.query['mode'] if isinstance(opts['mode'], util.string_types): mode = opts['mode'].upper() if mode == 'SYSDBA': opts['mode'] = self.dbapi.SYSDBA elif mode == 'SYSOPER': opts['mode'] = self.dbapi.SYSOPER else: util.coerce_kw_type(opts, 'mode', int) return ([], opts)
def _do_check(state, value, oldvalue, initiator): if value is not None: hasparent = initiator.hasparent(attributes.instance_state(value)) if hasparent and oldvalue is not value: raise sa_exc.InvalidRequestError( "Instance %s is already associated with an instance " "of %s via its %s attribute, and is only allowed a " "single parent." % (mapperutil.instance_str(value), state.class_, prop)) return value
def pre_exec(self): self._enable_identity_insert = False self._select_lastrowid = False self._lastrowid = None if self.isinsert: tbl = self.compiled.statement.table seq_column = tbl._autoincrement_column insert_has_sequence = seq_column is not None if insert_has_sequence: self._enable_identity_insert = ( seq_column.key in self.compiled_parameters[0] ) else: self._enable_identity_insert = False # _select_lastrowid logic # based on: https://github.com/sqlalchemy/sqlalchemy/ # blob/3ab2364e78641c4f0e4b6456afc2cbed39b0d0e6/lib/sqlalchemy/ # dialects/mssql/base.py#L1506-L1512 self._select_lastrowid = ( not self.compiled.inline and insert_has_sequence and not self.compiled.returning and not self._enable_identity_insert and not self.executemany ) if self._select_lastrowid: # https://github.com/FreeTDS/freetds/issues/337#issuecomment-640070962 self.statement += "\n SELECT @@identity" if self._enable_identity_insert: self.cursor.execute( "SET IDENTITY_INSERT %s ON" % self.dialect.identifier_preparer.format_table(tbl) ) if self.isddl: # TODO: to enhance this, we can detect "ddl in tran" on the # database settings. this error message should be improved # to include a note about that. if not self.should_autocommit: raise exc.InvalidRequestError( "The Sybase dialect only supports " "DDL in 'autocommit' mode at this time." ) self.root_connection.engine.logger.info( "AUTOCOMMIT (Assuming no Sybase 'ddl in tran')" ) self.set_ddl_autocommit( self.root_connection.connection.connection, True )
def limit_clause(self, select): # The docs say offsets are supported with LIMIT. But they're not. # TODO: maybe emulate by adding a ROWNO/ROWNUM predicate? if self.is_subquery(): # sub queries need TOP return '' elif select._offset: raise exc.InvalidRequestError( 'MaxDB does not support LIMIT with an offset.') else: return ' \n LIMIT %s' % (select._limit, )
def _revalidate_connection(self): if self._Connection__can_reconnect and self._Connection__invalid: if self._Connection__transaction is not None: raise sqla_exc.InvalidRequestError( "Can't reconnect until invalid " "transaction is rolled back") self._Connection__connection = self.engine.raw_connection( _connection=self) self._Connection__invalid = False return self._Connection__connection raise sqla_exc.ResourceClosedError("This Connection is closed")
def get_select_precolumns(self, select, **kwargs): """Teradata uses TOP instead of LIMIT """ if select._distinct or select._limit is not None: s = select._distinct and "DISTINCT " or "" if select._limit is not None: s += "TOP %d " % (select._limit) if select._offset is not None: raise exc.InvalidRequestError('Teradata does not support LIMIT with an offset') return s return compiler.SQLCompiler.get_select_precolumns(self, select)
def test_statement_error_no_code(self): try: raise sa_exceptions.DBAPIError.instance( 'select * from table', [{ "x": 1 }], sa_exceptions.InvalidRequestError("hello"), DatabaseError) except sa_exceptions.StatementError as err: eq_( str(err), "(sqlalchemy.exc.InvalidRequestError) hello " "[SQL: 'select * from table'] [parameters: [{'x': 1}]]") eq_(err.args, ("(sqlalchemy.exc.InvalidRequestError) hello", ))
def make_transient_to_detached(instance): ''' Moved from sqlalchemy newer version ''' state = attributes.instance_state(instance) if state.session_id or state.key: raise sa_exc.InvalidRequestError("Given object must be transient") state.key = state.mapper._identity_key_from_state(state) if state.deleted: del state.deleted state._commit_all(state.dict) state._expire_attributes(state.dict, state.unloaded)
def process(value): if not isinstance(value, (unicode, NoneType)): if assert_unicode == 'warn': util.warn("Unicode type received non-unicode bind " "param value %r" % value) return value else: raise exc.InvalidRequestError( "Unicode type received non-unicode bind param value %r" % value) else: return value
def remove(self, value, _sa_initiator=None): """Remove an item by value, consulting the keyfunc for the key.""" key = self.keyfunc(value) # Let self[key] raise if key is not in this collection # testlib.pragma exempt:__ne__ if not self.__contains__(key) or value not in self[key]: raise sa_exc.InvalidRequestError( "Can not remove '%s': collection holds '%s' for key '%s'. " "Possible cause: is the MappedCollection key function " "based on mutable properties or properties that only obtain " "values after flush?" % (value, self[key], key)) self.__getitem__(key, _sa_initiator).remove(value)
def process(value): if value is None: return None elif dialect.datetimeformat == 'internal': return datetime.date( *[int(v) for v in (value[0:4], value[4:6], value[6:8])]) elif dialect.datetimeformat == 'iso': return datetime.date( *[int(v) for v in (value[0:4], value[5:7], value[8:10])]) else: raise exc.InvalidRequestError( "datetimeformat '%s' is not supported." % (dialect.datetimeformat, ))
def invalidate(self, e=None): """Mark this connection as invalidated. The connection will be immediately closed. The containing ConnectionRecord will create a new connection when next used. """ if self.connection is None: raise exc.InvalidRequestError("This connection is closed") if self._connection_record is not None: self._connection_record.invalidate(e=e) self.connection = None self._close()
def info(self): """An info collection unique to this DB-API connection.""" try: return self._connection_record.info except AttributeError: if self.connection is None: raise exc.InvalidRequestError("This connection is closed") try: return self._detached_info except AttributeError: self._detached_info = value = {} return value
def process(value): if value is None: return None elif isinstance(value, basestring): return value elif dialect.datetimeformat == 'internal': return value.strftime("%H%M%S") elif dialect.datetimeformat == 'iso': return value.strftime("%H-%M-%S") else: raise exc.InvalidRequestError( "datetimeformat '%s' is not supported." % (dialect.datetimeformat, ))