SqlAlchemy 2.0 中文文档(七十四)(5)

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
简介: SqlAlchemy 2.0 中文文档(七十四)

SqlAlchemy 2.0 中文文档(七十四)(4)https://developer.aliyun.com/article/1562364


关键行为变化 - 核心

自定义运算符的类型行为已经统一

可以使用 Operators.op() 函数动态创建用户定义的运算符。以前,表达式对此类运算符的类型行为不一致,也无法控制。

而在 1.1 版本中,类似以下表达式将产生无返回类型的结果(假设 -%> 是数据库支持的某个特殊运算符):

>>> column("x", types.DateTime).op("-%>")(None).type
NullType()

其他类型将使用使用左侧类型作为返回类型的默认行为:

>>> column("x", types.String(50)).op("-%>")(None).type
String(length=50)

这些行为大多是偶然发生的,因此行为已与第二种形式保持一致,即默认返回类型与左侧表达式相同:

>>> column("x", types.DateTime).op("-%>")(None).type
DateTime()

由于大多数用户定义的运算符往往是“比较”运算符,通常是由 PostgreSQL 定义的许多特殊运算符之一,Operators.op.is_comparison 标志已修复,以遵循其允许返回类型为 Boolean 的文档行为,包括对 ARRAYJSON

>>> column("x", types.String(50)).op("-%>", is_comparison=True)(None).type
Boolean()
>>> column("x", types.ARRAY(types.Integer)).op("-%>", is_comparison=True)(None).type
Boolean()
>>> column("x", types.JSON()).op("-%>", is_comparison=True)(None).type
Boolean()

为了辅助布尔比较运算符,添加了一个新的简写方法 Operators.bool_op()。应优先使用此方法进行即时布尔运算符:

>>> print(column("x", types.Integer).bool_op("-%>")(5))
x  -%>  :x_1 
```### `literal_column()` 中的百分号现在有条件地转义
`literal_column` 结构现在根据使用的 DBAPI 是否使用了对百分号敏感的参数样式有条件地转义百分号字符(例如‘format’或‘pyformat’)。
以前,无法生成一个声明单个百分号的 `literal_column` 结构:
```py
>>> from sqlalchemy import literal_column
>>> print(literal_column("some%symbol"))
some%%symbol 

对于未设置为使用‘format’或‘pyformat’参数样式的方言,百分号现在不受影响;大多数 MySQL 方言等声明了其中一个参数样式的方言将继续适当转义:

>>> from sqlalchemy import literal_column
>>> print(literal_column("some%symbol"))
some%symbol
>>> from sqlalchemy.dialects import mysql
>>> print(literal_column("some%symbol").compile(dialect=mysql.dialect()))
some%%symbol 

作为这一变化的一部分,使用诸如 ColumnOperators.contains()ColumnOperators.startswith()ColumnOperators.endswith() 等运算符时,之前出现的加倍现象也被精细化,只在适当时发生。

#3740 ### 列级别的 COLLATE 关键字现在引用排序规则名称

collate()ColumnOperators.collate() 函数中的一个 bug,用于在语句级别提供临时列排序,已修复,其中一个大小写敏感的名称不会被引用:

stmt = select([mytable.c.x, mytable.c.y]).order_by(
    mytable.c.somecolumn.collate("fr_FR")
)

现在显示为:

SELECT  mytable.x,  mytable.y,
FROM  mytable  ORDER  BY  mytable.somecolumn  COLLATE  "fr_FR"

之前,大小写敏感的名称“fr_FR”不会被引用。目前,手动引用“fr_FR”名称会被检测到,因此手动引用标识符的应用程序应进行调整。请注意,此更改不会影响类型级别的排序的使用(例如,在表级别上指定在 String 类型上的排序),其中已经应用了引用。

#3785 ### 自定义运算符的输入行为已经保持一致

用户定义的运算符可以使用 Operators.op() 函数动态创建。先前,针对此类运算符的表达式的输入行为是不一致的,也无法控制。

在 1.1 中,例如以下表达式将产生一个没有返回类型的结果(假设 -%> 是数据库支持的某种特殊运算符):

>>> column("x", types.DateTime).op("-%>")(None).type
NullType()

其他类型将使用默认行为,即使用左侧类型作为返回类型:

>>> column("x", types.String(50)).op("-%>")(None).type
String(length=50)

这些行为大多是偶然发生的,因此行为已与第二种形式保持一致,即默认返回类型与左侧表达式相同:

>>> column("x", types.DateTime).op("-%>")(None).type
DateTime()

由于大多数用户定义的运算符倾向于是“比较”运算符,通常是 PostgreSQL 定义的许多特殊运算符之一,因此已修复了 Operators.op.is_comparison 标志,使其遵循其文档化的行为,即在所有情况下允许返回类型为 Boolean,包括对于 ARRAYJSON

>>> column("x", types.String(50)).op("-%>", is_comparison=True)(None).type
Boolean()
>>> column("x", types.ARRAY(types.Integer)).op("-%>", is_comparison=True)(None).type
Boolean()
>>> column("x", types.JSON()).op("-%>", is_comparison=True)(None).type
Boolean()

为了辅助布尔比较运算符,添加了一个新的简写方法 Operators.bool_op()。应优先使用此方法进行即时布尔运算:

>>> print(column("x", types.Integer).bool_op("-%>")(5))
x  -%>  :x_1 

literal_column() 中的百分号现在有条件地转义

literal_column 构造现在有条件地转义百分号字符,取决于正在使用的 DBAPI 是否使用了对百分号敏感的参数样式(例如‘format’或‘pyformat’)。

以前,无法生成一个声明了单个百分号的literal_column 构造:

>>> from sqlalchemy import literal_column
>>> print(literal_column("some%symbol"))
some%%symbol 

对于未设置为使用‘format’或‘pyformat’参数样式的方言,百分号现在不受影响;大多数 MySQL 方言等声明了这些参数样式的方言将继续适当地进行转义:

>>> from sqlalchemy import literal_column
>>> print(literal_column("some%symbol"))
some%symbol
>>> from sqlalchemy.dialects import mysql
>>> print(literal_column("some%symbol").compile(dialect=mysql.dialect()))
some%%symbol 

作为这一变化的一部分,使用ColumnOperators.contains()ColumnOperators.startswith()ColumnOperators.endswith()等操作符时,之前出现的加倍现象也被优化为仅在适当时发生。

#3740

列级别的 COLLATE 关键字现在引用了排序规则名称

修复了collate()ColumnOperators.collate()函数中的一个错误,用于在语句级别提供临时列排序规则,其中一个区分大小写的名称将不会被引用:

stmt = select([mytable.c.x, mytable.c.y]).order_by(
    mytable.c.somecolumn.collate("fr_FR")
)

现在呈现为:

SELECT  mytable.x,  mytable.y,
FROM  mytable  ORDER  BY  mytable.somecolumn  COLLATE  "fr_FR"

以前,区分大小写的名称“fr_FR”将不会被引用。目前,不会检测手动引用“fr_FR”名称,因此手动引用标识符的应用程序应进行调整。请注意,此更改不影响在类型级别使用排序规则(例如在数据类型上指定的String在表级别),其中已经应用了引用。

#3785

方言改进和变更 - PostgreSQL

支持批处理模式 / 快速执行助手

psycopg2 的 cursor.executemany() 方法被认为性能较差,特别是在 INSERT 语句中。为了缓解这一问题,psycopg2 添加了快速执行助手,通过批量发送多个 DML 语句来减少服务器往返次数。SQLAlchemy 1.2 现在包括对这些助手的支持,可以在Engine 使用 cursor.executemany() 对多个参数集合执行语句时透明地使用。该功能默认关闭,可以通过在 create_engine() 上使用 use_batch_mode 参数来启用:

engine = create_engine(
    "postgresql+psycopg2://scott:tiger@host/dbname", use_batch_mode=True
)

目前该功能被视为实验性质,但可能在未来的版本中默认开启。

另请参阅

Psycopg2 快速执行助手

#4109 ### 支持 INTERVAL 中字段规范的支持,包括完整反射

PostgreSQL 的 INTERVAL 数据类型中的“fields”规范允许指定要存储的间隔的哪些字段,包括“YEAR”、“MONTH”、“YEAR TO MONTH”等值。INTERVAL 数据类型现在允许指定这些值:

from sqlalchemy.dialects.postgresql import INTERVAL
Table("my_table", metadata, Column("some_interval", INTERVAL(fields="DAY TO SECOND")))

此外,所有 INTERVAL 数据类型现在都可以独立于存在的“fields”规范进行反射;数据类型本身的“fields”参数也将存在:

>>> inspect(engine).get_columns("my_table")
[{'comment': None,
 'name': u'some_interval', 'nullable': True,
 'default': None, 'autoincrement': False,
 'type': INTERVAL(fields=u'day to second')}]

#3959 ### 支持批处理模式 / 快速执行助手

psycopg2 的 cursor.executemany() 方法被认为性能较差,特别是在 INSERT 语句中。为了缓解这一问题,psycopg2 添加了快速执行助手,通过批量发送多个 DML 语句来减少服务器往返次数。SQLAlchemy 1.2 现在包括对这些助手的支持,可以在Engine 使用 cursor.executemany() 对多个参数集合执行语句时透明地使用。该功能默认关闭,可以通过在 create_engine() 上使用 use_batch_mode 参数来启用:

engine = create_engine(
    "postgresql+psycopg2://scott:tiger@host/dbname", use_batch_mode=True
)

目前该功能被视为实验性质,但可能在未来的版本中默认开启。

另请参阅

Psycopg2 快速执行助手

#4109

支持 INTERVAL 中字段规范的支持,包括完整反射

PostgreSQL 的 INTERVAL 数据类型中的“fields”指定符允许指定要存储的间隔的哪些字段,包括“YEAR”、“MONTH”、“YEAR TO MONTH”等值。INTERVAL 数据类型现在允许指定这些值:

from sqlalchemy.dialects.postgresql import INTERVAL
Table("my_table", metadata, Column("some_interval", INTERVAL(fields="DAY TO SECOND")))

此外,现在可以独立于“fields”指定符反映所有 INTERVAL 数据类型;数据类型本身的“fields”参数也将存在:

>>> inspect(engine).get_columns("my_table")
[{'comment': None,
 'name': u'some_interval', 'nullable': True,
 'default': None, 'autoincrement': False,
 'type': INTERVAL(fields=u'day to second')}]

#3959

方言改进和变更 - MySQL

支持 INSERT…ON DUPLICATE KEY UPDATE

MySQL 支持的 INSERTON DUPLICATE KEY UPDATE 子句现在可以使用 MySQL 特定版本的 Insert 对象来支持,通过 sqlalchemy.dialects.mysql.dml.insert()。这个 Insert 子类添加了一个新方法 Insert.on_duplicate_key_update(),实现了 MySQL 的语法:

from sqlalchemy.dialects.mysql import insert
insert_stmt = insert(my_table).values(id="some_id", data="some data to insert")
on_conflict_stmt = insert_stmt.on_duplicate_key_update(
    data=insert_stmt.inserted.data, status="U"
)
conn.execute(on_conflict_stmt)

以上内容将呈现为:

INSERT  INTO  my_table  (id,  data)
VALUES  (:id,  :data)
ON  DUPLICATE  KEY  UPDATE  data=VALUES(data),  status=:status_1

另请参阅

INSERT…ON DUPLICATE KEY UPDATE (Upsert)

#4009 ### 支持 INSERT…ON DUPLICATE KEY UPDATE

MySQL 支持的 INSERTON DUPLICATE KEY UPDATE 子句现在可以使用 MySQL 特定版本的 Insert 对象来支持,通过 sqlalchemy.dialects.mysql.dml.insert()。这个 Insert 子类添加了一个新方法 Insert.on_duplicate_key_update(),实现了 MySQL 的语法:

from sqlalchemy.dialects.mysql import insert
insert_stmt = insert(my_table).values(id="some_id", data="some data to insert")
on_conflict_stmt = insert_stmt.on_duplicate_key_update(
    data=insert_stmt.inserted.data, status="U"
)
conn.execute(on_conflict_stmt)

以上内容将呈现为:

INSERT  INTO  my_table  (id,  data)
VALUES  (:id,  :data)
ON  DUPLICATE  KEY  UPDATE  data=VALUES(data),  status=:status_1

另请参阅

INSERT…ON DUPLICATE KEY UPDATE (Upsert)

#4009

方言改进和变更 - Oracle

cx_Oracle 方言、类型系统的重大重构

随着 cx_Oracle DBAPI 的 6.x 系列的推出,SQLAlchemy 的 cx_Oracle 方言已经重新设计和简化,以利用 cx_Oracle 的最新改进,并放弃了在 cx_Oracle 的 5.x 系列之前更相关的模式的支持。

  • 支持的最低 cx_Oracle 版本现在是 5.1.3;推荐使用 5.3 或最新的 6.x 系列。
  • 数据类型的处理已经重构。cursor.setinputsizes() 方法不再用于除 LOB 类型之外的任何数据类型,根据 cx_Oracle 的开发人员的建议。因此,参数 auto_setinputsizesexclude_setinputsizes 已被弃用,不再起作用。
  • coerce_to_decimal 标志设置为 False 时,表示不应发生对具有精度和标度的数值类型进行到 Decimal 的强制转换,仅影响未定义类型(例如,没有 TypeEngine 对象的普通字符串)的语句。现在,包含 Numeric 类型或子类型的 Core 表达式将遵循该类型的十进制强制转换规则。
  • “两阶段”事务支持在方言中已经在 cx_Oracle 的 6.x 系列中被删除,现在已完全移除,因为这个功能从未正确工作过,也不太可能被用于生产环境。因此,allow_twophase 方言标志已被弃用,也不再起作用。
  • 修复了涉及 RETURNING 中存在的列键的错误。给定如下语句:
result = conn.execute(table.insert().values(x=5).returning(table.c.a, table.c.b))
  • 以前,结果中每行的键是 ret_0ret_1,这些是 cx_Oracle RETURNING 实现内部的标识符。现在,键将是 ab,这是其他方言所期望的。
  • cx_Oracle 的 LOB 数据类型将返回值表示为 cx_Oracle.LOB 对象,这是一个与游标关联的代理,通过 .read() 方法返回最终数据值。在历史上,如果在这些 LOB 对象被消耗之前读取了更多行(特别是在读取了比 cursor.arraysize  值更多的行,导致读取了一批新行),这些 LOB 对象会引发错误“在后续获取后 LOB 变量不再有效”。SQLAlchemy  通过在其类型系统内部自动调用 .read(),以及使用一个特殊的 BufferedColumnResultSet 来解决这个问题,该对象将确保在使用 cursor.fetchmany()cursor.fetchall() 这样的调用时,这些数据被缓冲。
    方言现在使用 cx_Oracle outputtypehandler 来处理这些 .read() 调用,以便无论获取多少行,它们始终被立即调用,因此不再会发生此错误。因此,BufferedColumnResultSet 的使用,以及一些其他特定于此用例的 Core ResultSet 内部机制已被移除。类型对象也变得更简化,因为它们不再需要处理二进制列结果。
    另外,cx_Oracle 6.x 已经删除了此错误发生的条件,因此不再可能发生此错误。在 SQLAlchemy 中,如果很少(如果有的话)使用了 auto_convert_lobs=False 选项,并且在 LOB 对象可以被消耗之前读取了更多行,则可能会发生错误。升级到 cx_Oracle 6.x 将解决这个问题。### Oracle 唯一性、检查约束现在已反映。

UNIQUE 和 CHECK 约束现在通过 Inspector.get_unique_constraints()Inspector.get_check_constraints() 反映出来。反映的 Table 对象现在也将包括 CheckConstraint 对象。有关此处行为怪癖的信息,请参阅 约束反射,包括大多数 Table 对象仍不会包括任何 UniqueConstraint 对象,因为这些通常通过 Index 表示。

另请参见

约束反射

#4003 ### Oracle 外键约束名称现在是“名称标准化”

在表反射期间传递给 ForeignKeyConstraint 对象的外键约束名称以及在 Inspector.get_foreign_keys() 方法中将会“名称标准化”,即以小写形式表示以进行大小写不敏感的命名,而不是 Oracle 使用的原始大写格式:

>>> insp.get_indexes("addresses")
[{'unique': False, 'column_names': [u'user_id'],
 'name': u'address_idx', 'dialect_options': {}}]
>>> insp.get_pk_constraint("addresses")
{'name': u'pk_cons', 'constrained_columns': [u'id']}
>>> insp.get_foreign_keys("addresses")
[{'referred_table': u'users', 'referred_columns': [u'id'],
 'referred_schema': None, 'name': u'user_id_fk',
 'constrained_columns': [u'user_id']}]

以前,外键约束的结果看起来像:

[
    {
        "referred_table": "users",
        "referred_columns": ["id"],
        "referred_schema": None,
        "name": "USER_ID_FK",
        "constrained_columns": ["user_id"],
    }
]

上述内容可能会在特别是与 Alembic autogenerate 一起创建问题。

#3276 ### cx_Oracle 方言、类型系统的重大重构

随着 cx_Oracle DBAPI 推出的 6.x 系列,SQLAlchemy 的 cx_Oracle 方言已经进行了重构和简化,以利用 cx_Oracle 的最新改进,并放弃了在 cx_Oracle 5.x 系列之前更相关的模式的支持。

  • 支持的最低 cx_Oracle 版本现在是 5.1.3;建议使用 5.3 或最新的 6.x 系列。
  • 数据类型的处理已经重构。cursor.setinputsizes() 方法现在仅用于 LOB 类型,根据 cx_Oracle 的开发人员的建议。因此,参数 auto_setinputsizesexclude_setinputsizes 已被弃用,不再起作用。
  • 当设置为 False 时,coerce_to_decimal 标志表示不应进行具有精度和标度的数字类型到 Decimal 的强制转换,仅影响未类型化的(例如,没有 TypeEngine 对象的普通字符串)语句。包含 Numeric 类型或子类型的 Core 表达式现在将遵循该类型的十进制强制转换规则。
  • 方言中的“两阶段”事务支持已经在 cx_Oracle 的 6.x 系列中删除,因为这个功能从未正确工作过,并且不太可能已经投入生产使用。因此,allow_twophase 方言标志已被弃用,也不再起作用。
  • 修复了涉及 RETURNING 的列键存在的错误。给定以下语句:
result = conn.execute(table.insert().values(x=5).returning(table.c.a, table.c.b))
  • 以前,结果中每行的键是 ret_0ret_1,这是 cx_Oracle RETURNING 实现的内部标识符。现在,键将是 ab,这是其他方言所期望的。
  • cx_Oracle 的 LOB 数据类型将返回值表示为 cx_Oracle.LOB 对象,它是一个与游标关联的代理,通过 .read() 方法返回最终数据值。历史上,如果在这些 LOB 对象被消耗之前读取了更多的行(具体来说,如果读取的行数超过了 cursor.arraysize  的值,这会导致读取一批新的行),这些 LOB 对象将引发错误“在后续获取后 LOB 变量不再有效”。SQLAlchemy  解决了这个问题,通过在其类型系统中自动调用 .read() 来处理这些 LOB,并使用特殊的 BufferedColumnResultSet 确保这些数据被缓冲,以防使用了 cursor.fetchmany()cursor.fetchall() 这样的调用。
    方言现在使用 cx_Oracle 的 outputtypehandler 来处理这些 .read() 调用,以便无论读取多少行,它们都始终被提前调用,因此不再会发生此错误。因此,已删除了 BufferedColumnResultSet 的使用,以及一些特定于此用例的 Core ResultSet 的其他内部内容。由于不再需要处理二进制列结果,类型对象也变得简化了。
    另外,cx_Oracle 6.x 已经删除了此错误在任何情况下发生的条件,因此该错误不再可能发生。在 SQLAlchemy 中,该错误可能发生在很少(如果有的话)使用了 auto_convert_lobs=False 选项,并且与之前的 cx_Oracle 5.x 系列一起使用,以及在 LOB 对象可以被消耗之前读取了更多行的情况下。升级到 cx_Oracle 6.x 将解决该问题。

Oracle 唯一性、检查约束现在已反映

唯一约束和检查约束现在通过 Inspector.get_unique_constraints()Inspector.get_check_constraints() 反映出来。一个反映的 Table 对象现在也将包括 CheckConstraint 对象。请参阅 Constraint Reflection 中的注意事项,了解这里的行为怪癖,包括大多数 Table 对象仍将不包括任何 UniqueConstraint 对象,因为这些通常通过 Index 表示。

另请参阅

Constraint Reflection

#4003

Oracle 外键约束名称现在已经“名称标准化”

在表反射期间传递给 ForeignKeyConstraint 对象以及在 Inspector.get_foreign_keys() 方法内部的外键约束的名称现在将被“名称标准化”,即,以小写形式表示以便于不区分大小写的名称,而不是 Oracle 使用的原始大写格式:

>>> insp.get_indexes("addresses")
[{'unique': False, 'column_names': [u'user_id'],
 'name': u'address_idx', 'dialect_options': {}}]
>>> insp.get_pk_constraint("addresses")
{'name': u'pk_cons', 'constrained_columns': [u'id']}
>>> insp.get_foreign_keys("addresses")
[{'referred_table': u'users', 'referred_columns': [u'id'],
 'referred_schema': None, 'name': u'user_id_fk',
 'constrained_columns': [u'user_id']}]

以前,外键结果看起来像是:

[
    {
        "referred_table": "users",
        "referred_columns": ["id"],
        "referred_schema": None,
        "name": "USER_ID_FK",
        "constrained_columns": ["user_id"],
    }
]

上述情况可能会特别在 Alembic autogenerate 方面造成问题。

#3276

方言改进和更改 - SQL Server

支持具有嵌入点的 SQL Server 架构名称

SQL Server  方言有一种行为,即假定带有点的架构名称是“数据库”。“所有者”标识符对,这在表和组件反射操作以及在呈现架构名称的引用时必须在这些单独的组件之间进行拆分,以使这两个符号分别引用。现在可以使用方括号传递架构参数以手动指定此拆分发生的位置,允许包含一个或多个点的数据库和/或所有者名称:

Table("some_table", metadata, Column("q", String(50)), schema="[MyDataBase.dbo]")

上表将考虑“owner”为MyDataBase.dbo,在呈现时也将被引用,并且“database”为 None。要分别引用数据库名称和所有者,请使用两对括号:

Table(
    "some_table",
    metadata,
    Column("q", String(50)),
    schema="[MyDataBase.SomeDB].[MyDB.owner]",
)

此外,当传递给 SQL Server 方言的“schema”时,现在会尊重quoted_name构造;如果引号标志为 True,则给定的符号不会在点上拆分,并且将被解释为“owner”。

另请参见

多部分模式名称

#2626

支持 AUTOCOMMIT 隔离级别

PyODBC 和 pymssql 方言现在都支持由Connection.execution_options()设置的“AUTOCOMMIT”隔离级别,这将在 DBAPI 连接对象上建立正确的标志。

支持带有嵌入点的 SQL Server 模式名称

SQL Server  方言具有这样的行为,即假定具有其中的点的模式名称是“数据库”。“所有者”标识符对,这在表和组件反射操作以及在呈现模式名称的引用时必须将这两个符号分开时发生,以便分别引用这两个符号。现在可以使用括号传递模式参数以手动指定此拆分发生的位置,允许数据库和/或所有者名称本身包含一个或多个点:

Table("some_table", metadata, Column("q", String(50)), schema="[MyDataBase.dbo]")

上表将考虑“owner”为MyDataBase.dbo,在呈现时也将被引用,并且“database”为 None。要分别引用数据库名称和所有者,请使用两对括号:

Table(
    "some_table",
    metadata,
    Column("q", String(50)),
    schema="[MyDataBase.SomeDB].[MyDB.owner]",
)

此外,当传递给 SQL Server 方言的“schema”时,现在会尊重quoted_name构造;如果引号标志为 True,则给定的符号不会在点上拆分,并且将被解释为“owner”。

另请参见

多部分模式名称

#2626

支持 AUTOCOMMIT 隔离级别

PyODBC 和 pymssql 方言现在都支持由Connection.execution_options()设置的“AUTOCOMMIT”隔离级别,这将在 DBAPI 连接对象上建立正确的标志。

以前,结果中每行的键是 `ret_0` 和 `ret_1`,这是 cx_Oracle RETURNING 实现的内部标识符。现在,键将是 `a` 和 `b`,这是其他方言所期望的。
+   cx_Oracle 的 LOB 数据类型将返回值表示为 `cx_Oracle.LOB` 对象,它是一个与游标关联的代理,通过 `.read()` 方法返回最终数据值。历史上,如果在这些 LOB 对象被消耗之前读取了更多的行(具体来说,如果读取的行数超过了 cursor.arraysize 的值,这会导致读取一批新的行),这些 LOB 对象将引发错误“在后续获取后 LOB 变量不再有效”。SQLAlchemy 解决了这个问题,通过在其类型系统中自动调用 `.read()` 来处理这些 LOB,并使用特殊的 `BufferedColumnResultSet` 确保这些数据被缓冲,以防使用了 `cursor.fetchmany()` 或 `cursor.fetchall()` 这样的调用。
   方言现在使用 cx_Oracle 的 outputtypehandler 来处理这些 `.read()` 调用,以便无论读取多少行,它们都始终被提前调用,因此不再会发生此错误。因此,已删除了 `BufferedColumnResultSet` 的使用,以及一些特定于此用例的 Core `ResultSet` 的其他内部内容。由于不再需要处理二进制列结果,类型对象也变得简化了。
   另外,cx_Oracle 6.x 已经删除了此错误在任何情况下发生的条件,因此该错误不再可能发生。在 SQLAlchemy 中,该错误可能发生在很少(如果有的话)使用了 `auto_convert_lobs=False` 选项,并且与之前的 cx_Oracle 5.x 系列一起使用,以及在 LOB 对象可以被消耗之前读取了更多行的情况下。升级到 cx_Oracle 6.x 将解决该问题。
### Oracle 唯一性、检查约束现在已反映
唯一约束和检查约束现在通过 `Inspector.get_unique_constraints()` 和 `Inspector.get_check_constraints()` 反映出来。一个反映的 `Table` 对象现在也将包括 `CheckConstraint` 对象。请参阅 Constraint Reflection 中的注意事项,了解这里的行为怪癖,包括大多数 `Table` 对象仍将不包括任何 `UniqueConstraint` 对象,因为这些通常通过 `Index` 表示。
另请参阅
Constraint Reflection
[#4003](https://www.sqlalchemy.org/trac/ticket/4003)
### Oracle 外键约束名称现在已经“名称标准化”
在表反射期间传递给 `ForeignKeyConstraint` 对象以及在 `Inspector.get_foreign_keys()` 方法内部的外键约束的名称现在将被“名称标准化”,即,以小写形式表示以便于不区分大小写的名称,而不是 Oracle 使用的原始大写格式:
```py
>>> insp.get_indexes("addresses")
[{'unique': False, 'column_names': [u'user_id'],
'name': u'address_idx', 'dialect_options': {}}]
>>> insp.get_pk_constraint("addresses")
{'name': u'pk_cons', 'constrained_columns': [u'id']}
>>> insp.get_foreign_keys("addresses")
[{'referred_table': u'users', 'referred_columns': [u'id'],
'referred_schema': None, 'name': u'user_id_fk',
'constrained_columns': [u'user_id']}]

以前,外键结果看起来像是:

[
    {
        "referred_table": "users",
        "referred_columns": ["id"],
        "referred_schema": None,
        "name": "USER_ID_FK",
        "constrained_columns": ["user_id"],
    }
]

上述情况可能会特别在 Alembic autogenerate 方面造成问题。

#3276

方言改进和更改 - SQL Server

支持具有嵌入点的 SQL Server 架构名称

SQL Server  方言有一种行为,即假定带有点的架构名称是“数据库”。“所有者”标识符对,这在表和组件反射操作以及在呈现架构名称的引用时必须在这些单独的组件之间进行拆分,以使这两个符号分别引用。现在可以使用方括号传递架构参数以手动指定此拆分发生的位置,允许包含一个或多个点的数据库和/或所有者名称:

Table("some_table", metadata, Column("q", String(50)), schema="[MyDataBase.dbo]")

上表将考虑“owner”为MyDataBase.dbo,在呈现时也将被引用,并且“database”为 None。要分别引用数据库名称和所有者,请使用两对括号:

Table(
    "some_table",
    metadata,
    Column("q", String(50)),
    schema="[MyDataBase.SomeDB].[MyDB.owner]",
)

此外,当传递给 SQL Server 方言的“schema”时,现在会尊重quoted_name构造;如果引号标志为 True,则给定的符号不会在点上拆分,并且将被解释为“owner”。

另请参见

多部分模式名称

#2626

支持 AUTOCOMMIT 隔离级别

PyODBC 和 pymssql 方言现在都支持由Connection.execution_options()设置的“AUTOCOMMIT”隔离级别,这将在 DBAPI 连接对象上建立正确的标志。

支持带有嵌入点的 SQL Server 模式名称

SQL Server  方言具有这样的行为,即假定具有其中的点的模式名称是“数据库”。“所有者”标识符对,这在表和组件反射操作以及在呈现模式名称的引用时必须将这两个符号分开时发生,以便分别引用这两个符号。现在可以使用括号传递模式参数以手动指定此拆分发生的位置,允许数据库和/或所有者名称本身包含一个或多个点:

Table("some_table", metadata, Column("q", String(50)), schema="[MyDataBase.dbo]")

上表将考虑“owner”为MyDataBase.dbo,在呈现时也将被引用,并且“database”为 None。要分别引用数据库名称和所有者,请使用两对括号:

Table(
    "some_table",
    metadata,
    Column("q", String(50)),
    schema="[MyDataBase.SomeDB].[MyDB.owner]",
)

此外,当传递给 SQL Server 方言的“schema”时,现在会尊重quoted_name构造;如果引号标志为 True,则给定的符号不会在点上拆分,并且将被解释为“owner”。

另请参见

多部分模式名称

#2626

支持 AUTOCOMMIT 隔离级别

PyODBC 和 pymssql 方言现在都支持由Connection.execution_options()设置的“AUTOCOMMIT”隔离级别,这将在 DBAPI 连接对象上建立正确的标志。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
4月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(七十四)(3)
SqlAlchemy 2.0 中文文档(七十四)
47 1
|
4月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(六十八)(3)
SqlAlchemy 2.0 中文文档(六十八)
26 0
|
4月前
|
SQL Python
SqlAlchemy 2.0 中文文档(七十四)(4)
SqlAlchemy 2.0 中文文档(七十四)
31 6
|
4月前
|
SQL 关系型数据库 MySQL
SqlAlchemy 2.0 中文文档(七十四)(1)
SqlAlchemy 2.0 中文文档(七十四)
66 1
|
4月前
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(七十四)(2)
SqlAlchemy 2.0 中文文档(七十四)
36 1
|
4月前
|
SQL JSON 关系型数据库
SqlAlchemy 2.0 中文文档(四十八)(4)
SqlAlchemy 2.0 中文文档(四十八)
46 0
|
4月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(四十八)(5)
SqlAlchemy 2.0 中文文档(四十八)
24 0
|
4月前
|
SQL 关系型数据库 PostgreSQL
SqlAlchemy 2.0 中文文档(四十八)(3)
SqlAlchemy 2.0 中文文档(四十八)
61 0
|
4月前
|
SQL 关系型数据库 PostgreSQL
SqlAlchemy 2.0 中文文档(四十八)(1)
SqlAlchemy 2.0 中文文档(四十八)
41 0
|
4月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(四十八)(2)
SqlAlchemy 2.0 中文文档(四十八)
70 0