Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Misleading SQLServerException: Parameter was not defined for stored procedure #608

Closed
cnsgithub opened this issue Jan 24, 2018 · 8 comments

Comments

@cnsgithub
Copy link

Driver version or jar name

mssql-jdbc:6.3.6.jre8-previous (also tested with latest stable release)

SQL Server version

SQL Server 2017 (also tested with 2016)

Client operating system

Windows 8.1 (also tested with Windows Server 2012 R2)

Java/JVM version

Oracle 9.0.4 (also tested with Java 8)

Table schema

n/a

Problem description

When trying to execute a simple stored procedure providing one incoming parameter the driver gave me:

WARN: SQL Error: 0, SQLState: S1093
Jan 24, 2018 10:21:57 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Parameter id was not defined for stored procedure test.
Exception in thread "main" javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Error preparing CallableStatement
	at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:149)
	at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157)
	at org.hibernate.procedure.internal.ProcedureCallImpl.getResultList(ProcedureCallImpl.java:765)
	at org.hibernate.procedure.internal.ProcedureCallImpl.getSingleResult(ProcedureCallImpl.java:775)
	at Main.main(Main.java:10)
Caused by: org.hibernate.exception.GenericJDBCException: Error preparing CallableStatement
	at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
	at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
	at org.hibernate.procedure.internal.ProcedureCallImpl.buildOutputs(ProcedureCallImpl.java:456)
	at org.hibernate.procedure.internal.ProcedureCallImpl.getOutputs(ProcedureCallImpl.java:404)
	at org.hibernate.procedure.internal.ProcedureCallImpl.outputs(ProcedureCallImpl.java:663)
	at org.hibernate.procedure.internal.ProcedureCallImpl.getResultList(ProcedureCallImpl.java:751)
	... 2 more
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Parameter id was not defined for stored procedure test.
	at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:228)
	at com.microsoft.sqlserver.jdbc.SQLServerCallableStatement.findColumn(SQLServerCallableStatement.java:1499)
	at com.microsoft.sqlserver.jdbc.SQLServerCallableStatement.setInt(SQLServerCallableStatement.java:2682)
	at org.hibernate.type.descriptor.sql.IntegerTypeDescriptor$1.doBind(IntegerTypeDescriptor.java:52)
	at org.hibernate.type.descriptor.sql.BasicBinder.bind(BasicBinder.java:104)
	at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:400)
	at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:395)
	at org.hibernate.procedure.internal.AbstractParameterRegistrationImpl.prepare(AbstractParameterRegistrationImpl.java:335)
	at org.hibernate.procedure.internal.ProcedureCallImpl.buildOutputs(ProcedureCallImpl.java:444)
	... 5 more

This behavior seemed to me very weird since the same code has worked on production site for months - until I finally detected different object permissions of the executing user. The exception arises just in case of missing execute permission on the stored procedure and can therefore be simply fixed by just granting that permission. However, the exception is somewhat misleading, it should be changed to a more appropriate, meaningful one.

Although, the code producing the above stack trace is based on JPA/Hibernate, you should be able to reproduce it with plain JDBC as well - or even in SQL directly (see repro code below).

Entity (just for declaring the named stored procedure):

import javax.persistence.*;

@Entity
@NamedStoredProcedureQuery(procedureName = "test", name = "test", parameters = @StoredProcedureParameter(type = Integer.class, mode = ParameterMode.IN, name = "id"))
public class TestEnt {
	@Id
	private int id;
}

Main method for testing:

public class Main {
	private static final EntityManagerFactory emf = Persistence.createEntityManagerFactory("NewPersistenceUnit"); -- specified in JPA's persistence.xml

	public static void main(String[] args) {
		EntityManager entityManager = emf.createEntityManager();
		StoredProcedureQuery storedProcedureQuery = entityManager.createNamedStoredProcedureQuery("test");
		storedProcedureQuery.setParameter("id", 1);
		Object res = storedProcedureQuery.getSingleResult();
		System.out.println("Return value " + res);
	}
}

I think the misleading exception arises because SQLServerCallableStatement.findColumn("id") retrieves zero results from sp_proc_columns and assumes that there is no column named 'id' in the stored procedure. This happens before actually trying to execute the user stored procedure which would probably lead to a meaningful 'The EXECUTE permission was denied' message.

Expected behavior and actual behavior

Expected: 'The EXECUTE permission was denied'
Actual: 'Parameter was not defined for stored procedure'

Repro code

create database test_sp
go

use test_sp
go

create proc test @id int as select @id
go

create login reader with password='password_reader',default_database=test_sp
create user reader for login reader
alter role db_datareader add member reader
go

create login executor with password='password_executor',default_database=test_sp
create user executor for login executor
alter role db_datareader add member executor
grant execute on test to executor
go

exec as user='executor'
exec test @id=1 --works as expected
exec sp_sproc_columns @procedure_name=test , @ODBCVer=3 --works as expected: returns two rows, @RETURN_VALUE and @id
revert
go

exec as user='reader'
exec test @id=1 --works as expected: The EXECUTE permission was denied on the object 'test', database 'test_sp', schema 'dbo'.
exec sp_sproc_columns @procedure_name=test , @ODBCVer=3 --does NOT work as expected: returns zero rows instead of throwing an meaningful exception
revert
go

drop database test_sp
go
@cnsgithub
Copy link
Author

Maybe sp_helprotect (https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-helprotect-transact-sql) could be used beforehand to check if permission is granted?

@cnsgithub
Copy link
Author

Here's some repro code in plain JDBC:

package main.java;

import com.microsoft.sqlserver.jdbc.SQLServerException;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class MssqlJdbc608 {

	private static final String URL = "jdbc:sqlserver://<INSERT_URL_HERE>;databaseName=test_sp";

	@Rule
	public ExpectedException expectedEx = ExpectedException.none();

	@Test
	public void testPositionalParamPermissionDenied() throws SQLException {
		expectedEx.expect(SQLServerException.class);
		expectedEx.expectMessage("The EXECUTE permission was denied on the object 'test'");
		try (Connection conn = DriverManager.getConnection(URL, "reader", "password_reader")) {
			CallableStatement stmt = conn.prepareCall("{ call test(?) }");
			stmt.setInt(1,42);
			stmt.execute();
		}
	}

	//fails with 'Parameter id was not defined for stored procedure test.'
	@Test
	public void testNamedParamPermissionDenied() throws SQLException {
		expectedEx.expect(SQLServerException.class);
		expectedEx.expectMessage("The EXECUTE permission was denied on the object 'test'");
		try (Connection conn = DriverManager.getConnection(URL, "reader", "password_reader")) {
			CallableStatement stmt = conn.prepareCall("{ call test(?) }");
			stmt.setInt("id", 42);
			stmt.execute();
		}
	}

	@Test
	public void testNamedParamPermissionGranted() throws SQLException {
		try (Connection conn = DriverManager.getConnection(URL, "executor", "password_executor")) {
			CallableStatement stmt = conn.prepareCall("{ call test(?) }");
			stmt.setInt("id", 42);
			stmt.execute();
		}
	}

	public static boolean isPermissionGranted(Connection conn, String sprocName) throws SQLException {
		CallableStatement stmt = conn.prepareCall("{ call sp_helprotect(?) }");
		stmt.setString(1, sprocName);
		try {
			stmt.execute();
		}catch(SQLServerException ex) {
			return false;
		}
		return true;
	}

	@Test
	public void testHelprotectPermissionDenied() throws SQLException {
		try (Connection conn = DriverManager.getConnection(URL, "reader", "password_reader")) {
			Assert.assertFalse("Permission should not be granted", isPermissionGranted(conn, "test"));
		}
	}

	@Test
	public void testHelprotectPermissionGranted() throws SQLException {
		try (Connection conn = DriverManager.getConnection(URL, "executor", "password_executor")) {
			Assert.assertTrue("Permission should be granted", isPermissionGranted(conn, "test"));
		}
	}

}

@cheenamalhotra cheenamalhotra added the Under Review Used for pull requests under review label Jan 30, 2018
@rene-ye
Copy link
Member

rene-ye commented Feb 6, 2018

Hi @cnsgithub, thanks for your contribution and the detailed explanation. We have been able to reproduce the issue locally, and can confirm the behavior of the driver is not expected. A proposed change is under review. In the mean time, you can test the changes by pulling this branch. Please feel free to leave a comment regarding any concerns or issues you come across.

@rene-ye rene-ye closed this as completed Feb 6, 2018
@rene-ye rene-ye reopened this Feb 6, 2018
@cheenamalhotra cheenamalhotra added Waiting for Response Waiting for a reply from the original poster, or affiliated party and removed Under Review Used for pull requests under review labels Feb 9, 2018
@cnsgithub
Copy link
Author

Hi @rene-ye, I just tested your proposed fix and can confirm that everything works as expected now. Good work, thanks!

@cheenamalhotra cheenamalhotra removed the Waiting for Response Waiting for a reply from the original poster, or affiliated party label Feb 13, 2018
@rene-ye
Copy link
Member

rene-ye commented Mar 6, 2018

PR #635 has been merged and can be expected in the 6.5.0 release. Closing issue.

@nilaytiwari
Copy link

For which version this issue is fixed as of now . We are using 7.2.2.jre8 with RHEL OS and Open JDK facing same issue.

@mprins
Copy link

mprins commented May 25, 2020

as you can see in #1059 7.2.2 is also affected with something similar, fixed with #1064 states 7.3.1

@helderhernandez
Copy link

Using spring boot and sql server I got the following error:
SQL Error: 0, SQLState: S1093 The Document parameter was not defined for the USP_UpdateDocument stored procedure.

And I had my application.properties configured as follows:
spring.jpa.database-platform=org.hibernate.dialect.SQLServer2012Dialect
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Solution:
Assign EXECUTE permission to the User (with which to connect to the DB) on the stored procedure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants