从 Python/pyodbc 执行 sp_setapprole 失败

问题描述 投票:0回答:1

我无法通过 pyodbc 成功执行 SQL Server 存储过程

sp_setapprole

SQL Server 2019 Client OS: Windows 10 Python: v3.8 PyODBC: v4.0.34 SQLAlchemy: v1.4.39
最终用户安全最初是通过主动安全组进行管理的

Active Directory 组有一个 SQL Server 登录名,该组是 SQL Server 公共角色的成员,并且具有 SQL Server 的“连接 SQL”权限

此外,SQL Server 登录会映射到目标数据库中的用户帐户。该用户帐户是数据库“公共”角色的成员,并且仅被显式授予对数据库的“CONNECT”权限。

我已经在 SQL Server Management Studio (SSMS) 中测试了 SQL 代码,它可以正常工作,没有任何问题。

当我使用 Python/pyodbc 运行相同的代码时,它会生成我无法解决的错误。

注 1:错误消息因我使用的 ODBC 驱动程序而异。 (见下文)

SQL Server(SQLSRV32.DLL)

ERROR: An exception/error has occurred. Traceback (most recent call last): File "C:\Users\...\testcode.py", line ..., in ... cursor_obj.execute(sql, sp_val) pyodbc.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC SQL Server Driver][SQL Server]Application roles can only be activated at the ad hoc level. (15422) (SQLExecDirectW)")

SQL Server 本机客户端 11.0 (SQLNCLI11.DLL)

ERROR: An exception/error has occurred. Traceback (most recent call last): File "C:\Users\...\testcode.py", line ..., in ... cursor_obj.execute(sql, sp_val) pyodbc.ProgrammingError: ('42000', "[42000] [Microsoft][SQL Server Native Client 11.0][SQL Server]The procedure 'sys.sp_setapprole' cannot be executed within a transaction. (15002) (SQLExecDirectW)")

适用于 SQL Server 的 ODBC 驱动程序 17 (MSODBCSQL17.DLL)

ERROR: An exception/error has occurred. Traceback (most recent call last): File "C:\Users\...\testcode.py", line ..., in ... cursor_obj.execute(sql, sp_val) pyodbc.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]The procedure 'sys.sp_setapprole' cannot be executed within a transaction. (15002) (SQLExecDirectW)")
注 2:应用程序中的所有其他代码均有效。尽管与数据库的大部分交互是通过 SQL Alchemy 进行的,但使用 pyodbc 的其他例程可以正常工作。

注意 3:我不想多次运行“sp_setapprole”。在我对数据库执行任何其他操作之前,第一次尝试运行存储过程时会出现此问题。

注 4:db_engine 是从数据库连接字符串创建的 SQLAlchemy 引擎对象。生成上述第一条错误消息时,正在使用以下连接字符串:

connection_string = "DRIVER={SQL Server};SERVER=SQLserverName;DATABASE=MyDatabase;TRUSTED_CONNECTION=YES"
以下代码片段包含与 

sp_setapprole

 问题相关的代码:

app_role_info = {'@rolename' : 'MyAppRole', '@password' : 'AppRolePassword', '@fCreateCookie' : 1} # MSSQL True = 1 connection = db_engine.raw_connection() try: cursor_obj = connection.cursor() first_key = True sp_var=' ' sp_var_list=[] for k,v in app_role_info.items(): if not first_key: sp_var = sp_var + ', ' sp_var = sp_var + k + ' = ?' sp_val_list.append(v) first_key = False sp_val = tuple(sp_val_list) sql = f""" SET NOCOUNT ON; DECLARE @ret_value VARBINARY(8000); EXEC sp_setapprole {sp_var}, @cookie=@ret_value OUTPUT; SELECT @ret_value AS AppRoleCookie; """ cursor_obj.execute(sql, sp_val)
任何帮助/指导/智慧之言将不胜感激。

更新: 考虑到 @siggemannen 评论,我对代码进行了以下更改,并从等式中删除了 SQL Alchemy...

connection_string = "DRIVER={ODBC Driver 17 for SQL Server};SERVER=SQLserverName;DATABASE=MyDatabase;TRUSTED_CONNECTION=YES" app_role_info = {'@rolename' : 'MyAppRole', '@password' : 'AppRolePassword', '@fCreateCookie' : 1} # MSSQL True = 1 connection = pyodbc.connect(connection_string, autocommit=True) try: cursor_obj = connection.cursor() first_key = True sp_var=' ' sp_var_list=[] for k,v in app_role_info.items(): if not first_key: sp_var = sp_var + ', ' sp_var = sp_var + k + ' = ?' sp_val_list.append(v) first_key = False sp_val = tuple(sp_val_list) sql = f""" SET NOCOUNT ON; DECLARE @ret_value VARBINARY(8000); EXEC sp_setapprole {sp_var}, @cookie=@ret_value OUTPUT; SELECT @ret_value AS AppRoleCookie; """ cursor_obj.execute(sql, sp_val)
运行更新后的代码(上面)现在会导致以下异常/错误:

ERROR: An exception/error has occurred. Traceback (most recent call last): File "C:\Users\...\testcode.py", line ..., in ... cursor_obj.execute(sql, sp_val) pyodbc.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Application roles can only be activated at the ad hoc level. (15422) (SQLExecDirectW)")
该错误与本文前面的 ODBC Driver 17 for SQL Server 错误消息不同。

我相信@siggemannen 评论是正确的,并将自动提交设置为 True 纠正了事务/嵌套问题。我仍然不知道如何纠正“...临时级别”。错误。

sql-server stored-procedures pyodbc
1个回答
0
投票
PyOdbc 不支持

.callproc

,如文档中
所述,因此它可能无法按照您的方式工作。看来 sp_setapprole 根本无法从参数化批次中调用。
唯一可行的是使用 ODBC 调用转义语法:

tuple = (role_name, pass, "none", "odbc", true, null,) cursor_obj.execute("{call sp_setapprole(?, ?, ?, ?, ?, ?)}", tuple)

然后这些元组值中的最后一个应该成为输出参数

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.