pyodbc icon indicating copy to clipboard operation
pyodbc copied to clipboard

Failure in executing stored procedure with table valued parameter

Open sandeepnmenon opened this issue 5 years ago • 7 comments
trafficstars

Environment

To diagnose, we usually need to know the following, including version numbers. On Windows, be sure to specify 32-bit Python or 64-bit:

  • Python: 3.6.5
  • pyodbc: 4.0.28
  • OS: Windows 64-bit
  • DB: Microsoft SQL Server
  • driver: ODBC Driver 17 for SQL Server

Issue

Executing stored proc with table valued parameter (TVP) along with other parameters throws the following error

Executing sql:execute [dbo].[ExecuteMapping] @id=?, @values=?, @threshold=? Execute sql got error:('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Column, parameter, or variable # 2: Cannot find data type READONLY. (2715) (SQLExecDirectW); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Statement(s) could not be prepared. (8180); [42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Parameter or variable '@P2' has an invalid data type. (2724)")

TVP

CREATE TYPE [dbo].[MappingType] AS TABLE
(
  [t_entityid] UNIQUEIDENTIFIER NOT NULL,
  [t_threshold] FLOAT DEFAULT 0 
)
GO

Stored Procedure

PROCEDURE [dbo].[ExecuteMapping](
    @id uniqueidentifier,
    @values MappingType READONLY,
    @threshold float
)

Python code snippet

sql_connection = pyodbc.connect(connection_string)

mappingvalues = []
mappingvalues.append(["8e4d7360-9a46-ea11-a812-000d3a54419d",0.6])
mappingvalues.append(["ed0ef314-9a46-ea11-a812-000d3a8d88aa",0.6])

params = ("1a1d243c-a587-4d10-995e-cbbf6cad4dce", mappingvalues, 0.5)
sqlCmd = "execute [dbo].[ExecuteMapping] @id=?, @values=?, @threshold=?"

sqlCursor = sql_connection.cursor()
result = sqlCursor.execute(sqlCmd, params)

Executing this piece of code throws the above mentioned error

In the example given here, the TVP is the only parameter. How should I pass the parameteres when there are non TVP parameters along with TVP parameters for the stored procedure.

sandeepnmenon avatar Mar 30 '20 11:03 sandeepnmenon

I am unable to reproduce your issue using pyodbc 4.0.30.

# import, connect, etc. ...

# -- The stored procedure definition is:
#
# CREATE PROCEDURE [dbo].[ExecuteMapping](
#     @id uniqueidentifier,
#     @values MappingType READONLY,
#     @threshold float
# )
# AS
# BEGIN
# 	SET NOCOUNT ON;
# 	SELECT @id AS thing;
# END

mappingvalues = []
mappingvalues.append(["8e4d7360-9a46-ea11-a812-000d3a54419d",0.6])
mappingvalues.append(["ed0ef314-9a46-ea11-a812-000d3a8d88aa",0.6])

params = ("1a1d243c-a587-4d10-995e-cbbf6cad4dce", mappingvalues, 0.5)
sqlCmd = "execute [dbo].[ExecuteMapping] @id=?, @values=?, @threshold=?"

sqlCursor = cnxn.cursor()
result = sqlCursor.execute(sqlCmd, params)
print(result.fetchall())  # [('1A1D243C-A587-4D10-995E-CBBF6CAD4DCE', )]

gordthompson avatar Mar 30 '20 13:03 gordthompson

@gordthompson My pyodbc version was 4.0.28 I am ran an update command using conda update pyodbc now when I run pyodbc.version I see '4.0.0-unsupported' Then I removed the pyodbc using conda remove pyodbc

Used pip to install pip install pyodbc now pyodbc.version gives me 4.0.30

Still I am able to reproduce the error

sandeepnmenon avatar Mar 30 '20 17:03 sandeepnmenon

I can't reproduce this either, do you know what the server version is?

v-makouz avatar Apr 01 '20 17:04 v-makouz

I am having the same issue on pyodbc 4.0.30, and I'm running SQL Server 2017.

justinforlenza avatar Apr 06 '20 15:04 justinforlenza

@v-makouz I am running on SQL Server 12.0.2000.8

sandeepnmenon avatar Apr 13 '20 05:04 sandeepnmenon

I was able to replicate this. I get the failure when the the current database differs from where the stored procedure is defined... even you specify the fully qualified name!

E.x.: Assume the procedure mentioned above, [dbo].[ExecuteMapping], is in the database opera

If the connection is currently on the master database, the following code will fail cursor.execute("use master").nextset() cursor.execute("execute [opera].[dbo].[ExecuteMapping] @id=?, @values=?, @threshold=?", params)

If you change the database to the location of the stored procedure before executing, it will work. e.x.: cursor.execute("use opera").nextset() cursor.execute("execute [opera].[dbo].[ExecuteMapping] @id=?, @values=?, @threshold=?", params)

hjb417 avatar Dec 16 '20 02:12 hjb417

Okay, so it looks like this might be related to #595

gordthompson avatar Dec 16 '20 13:12 gordthompson

I get the failure when the the current database differs from where the stored procedure is defined... even you specify the fully qualified name!

If the stored procedure requires a user-defined Table type from the other database then that simply won't work as explained on Stack Overflow here:

Passing Table Valued parameter to stored procedure across different databases

If the stored procedure requires a user-defined Table type from a different schema in the current database then that issue was solved by #904 and released in pyodbc 4.0.32 as described in the wiki here.

gordthompson avatar Dec 21 '22 18:12 gordthompson