I have a problem with slick. It looks like after using it scala is not a statically typed language anymore:)
I used slick to call a SqlServer stored procedure that should return some values (and it does when I call the procedure directly in my SQL client). Unfortunately as you can see below the result is that I have a val with type Option[ProcessUserRoleDto], with ProcessUserRoleDto being just an ordinary case class, that in fact hold value of Some(1) and the '1' is of type java.lang.Integer. How is this possible? Do you have any ideas how to resolve this problem?
case class ProcessUserRoleDto(
processUserRoleId: Int,
processUserId: Int,
processRoleId: Int,
dataCollectionId: Int,
recognizingInstanceId: Int,
processRoleName: String,
dataCollectionName: String,
recognizingInstanceName: String,
isActive: Boolean)
def assign(dto: ProcessUserRoleAssignDto): Future[Option[ProcessUserRoleDto]] = {
val db = implicitly[SQLServerDriver.backend.DatabaseDef]
val sql = sql"exec EVO.spProcessUserRoleAssignToRecognizingInstance ${dto.ProcessUser_ID}, ${dto.ProcessRole_ID}, ${dto.RecognizingInstance_ID}"
implicit val registerProcessUserResult = GetResult { r =>
ProcessUserRoleDto(r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<, r.<<)
}
val resultFut: Future[Option[ProcessUserRoleDto]] = db
.run(sql.as[ProcessUserRoleDto])
.map(_.headOption)
val singleResult: Option[ProcessUserRoleDto] = Await.result(resultFut, 10 seconds)
println(s"singleResult = $singleResult")
// in runtime result was: Some(1)
// WTF ?!?
println(s"class inside Some() = ${singleResult.get.getClass.getCanonicalName}")
// in runtime result is: java.lang.Integer
println(s"Option[Class] = ${singleResult.map(_.getClass.getCanonicalName)}")
// here exception is thrown: java.lang.ClassCastException: java.lang.Integer cannot be cast to ProcessUserRoleDto
}
The stored procedure was executing an insert statement and then a select statement. I turns out that it confused the JDBC driver because the insert was returning the number of modified records and that adding "SET NOCOUNT ON" to the stored procedure stoped that (https://stackoverflow.com/a/4809815/1185138).
Still, slick quietly messed up types instead of reaturning a nice error message about that..
Related
I have several stored procedures in an application that were functioning perfectly (for years) until our recent upgrade from ColdFusion 2010 to ColdFusion 2016. Now, I am getting error messages of either too many parameters or a certain parameter is not a parameter is not contained in the procedure that is being called.
I have opted to upload some code so people can better understand what is actually happening. Still learning how to format code here so please forgive me if it is still lacking.
In both cases I have double checked the parameter lists in the stored procedure in the procedure calls and have found that they are all indeed correct. In fact, nothing has changed in this code for over 5 years. This behavior has only begun since the upgrade has taken place.
Below is the first example. I will list the procedure call (in cfscript)
first then the parameter list from the stored procedure and then the error message it produced:
public query function readStorage(numeric group1=0,numeric group2=0) {
local.group1Value = arguments.group1?arguments.group1:"";
local.group2Value = arguments.group2?arguments.group2:"";
spService = new storedproc();
spService.setDatasource(variables.dsn);
spService.setUsername(variables.userName);
spService.setPassword(variables.password);
spService.setProcedure("usp_readCompatibilityStorage");
spService.addParam(dbvarname="#group1Id",cfsqltype="cf_sql_integer"
, type="in",value=local.group1Value,null=!arguments.group1);
spService.addParam(dbvarname="#group2Id",cfsqltype="cf_sql_integer"
,type="in",value=local.group2Value,null=!arguments.group2);
spService.addProcResult(name="rs1",resultset=1);
local.result = spService.execute();
return local.result.getProcResultSets().rs1;
}
Below is the parameter list from the stored procedure:
#groupId1 int = NULL
,#groupId2 int = NULL
Below is the error message I get:
[Macromedia][SQLServer JDBC Driver][SQLServer]#group1Id is not a
parameter for procedure usp_readCompatibilityStorage.
Second Example:
public query function read(string cribIdList="",
numeric cribNumber=0,
string isAnnex="",
numeric siteId=0,
string parentCribIdList="",
numeric supervisorId=0,
numeric statusId=0,
string orderBy="cribNumber ASC") {
local.cribNumberValue = arguments.cribNumber?arguments.cribNumber:"";
local.siteIdValue = arguments.siteId?arguments.siteId:"";
local.superIdValue = arguments.supervisorId ? arguments.supervisorId:"";
local.statusIdValue = arguments.statusId ? arguments.statusId:"";
spService = new storedproc();
spService.setDatasource(variables.dsn);
spService.setUsername(variables.userName);
spService.setPassword(variables.password);
spService.setProcedure("usp_readCrib");
spService.addParam(dbvarname="#cribIdList",cfsqltype="cf_sql_varchar"
,type="in",value=arguments.cribIdList
,null=!len(arguments.cribIdList));
spService.addParam(dbvarname="#cribNumber",cfsqltype="cf_sql_integer"
,type="in",value=local.cribNumberValue
,null=!arguments.cribNumber);
spService.addParam(dbvarname="#isAnnex",cfsqltype="cf_sql_varchar"
,type="in",value=arguments.isAnnex,null=!len(arguments.isAnnex));
spService.addParam(dbvarname="#siteId",cfsqltype="cf_sql_integer"
,type="in",value=local.siteIdValue,null=!arguments.siteId);
spService.addParam(dbvarname="#parentCribIdList"
, cfsqltype="cf_sql_varchar", type="in"
, value=arguments.parentCribIdList
, null=!len(arguments.parentCribIdList));
spService.addParam(dbvarname="#supervisorId",
cfsqltype="cf_sql_integer", type="in",value=local.superIdValue
, null=!arguments.supervisorId);
spService.addParam(dbvarname="#statusId"
, cfsqltype="cf_sql_integer", type="in"
, value=local.statusIdValue, null=!arguments.statusId);
spService.addParam(dbvarname="#orderBy",cfsqltype="cf_sql_varchar"
, type="in",value=arguments.orderBy);
spService.addProcResult(name="rs1",resultset=1);
local.result = spService.execute();
return local.result.getProcResultSets().rs1;
}
Below is the parameter list from the stored procedure:
#cribIdList varchar(500) = NULL
,#cribNumber int = NULL
,#isAnnex varchar(3) = NULL
,#siteId int = NULL
,#parentCribIdList varchar(500) = NULL
,#supervisorId int = NULL
,#statusId int = NULL
,#orderBy varchar(50)
Below is the message returned from the server:
[Macromedia][SQLServer JDBC Driver][SQLServer]Procedure or function
usp_readCrib has too many arguments specified.
In the case of both errors, they seem to be occurring at the following path:
Error Details - struct
COLUMN 0
ID CFSTOREDPROC
LINE 489
RAW_TRACE at cfbase2ecfc235349229$funcINVOKETAG.runFunction(E:\ColdFusion2016\cfusion\CustomTags\com\adobe\coldfusion\base.cfc:489)
TEMPLATE E: \ColdFusion2016\cfusion\CustomTags\com\adobe\coldfusion\base.cfc
TYPE CFML````
ColdFusion 10 and greater limit the amount of parameters in a request to 100 by default. Fortunately this can be updated and changed to reflect the required amount of parameters you need for your stored procedures.
I'm using Spring Data JPA 1.10.2 with com.microsoft.sqlserver's sqljdbc 4.2. I get the following error:
o.h.engine.jdbc.spi.SqlExceptionHelper : Error preparing CallableStatement [User.pTest]
com.microsoft.sqlserver.jdbc.SQLServerException: The index 4 is out of range.
My entity class is:
#Entity
#NamedStoredProcedureQuery(name = "User.getUser", procedureName = "User.pTest", parameters = {
#StoredProcedureParameter(mode = ParameterMode.OUT, name = "session", type = byte[].class),
#StoredProcedureParameter(mode = ParameterMode.IN, name = "name", type = String.class),
#StoredProcedureParameter(mode = ParameterMode.IN, name = "date", type = Date.class)
})
#Data //lombok
public class User {
// serves no purpose other than to meet
// JPA requirement
#Id
private Long id;
}
The repository code is
public interface UserRepository extends Repository<User, Long> {
#Procedure("User.pTest")
byte[] getUserSession(#Param("name") String name,
#Param("date") Date date
);
}
My test code is as follows and when I run it I get the error:
#Test
public void testGettingApxSession() {
Calendar cal = new GregorianCalendar(2016,6,5);
byte[] b = userRepository.getUserSession("myName", cal.getTime());
}
How do I resolve the error?
Update
Forgot to include the relevant part of the SQL Server stored proc:
ALTER procedure [User].[pTest]
#session varbinary(max) out
,#name nvarchar(max) = null
,#opt nvarchar(max) = null
,#date datetime
as
begin
set #session = CAST(N'<?xml version="1.0" encoding="UTF-16" ?><Session session = 45"'/>' as varbinary(max))
end
If you specify the nth parameter, Microsoft SQL Server driver requires that you also include the other parameters up to n. For example, let's say your procedure has five parameters (in this order) each having a default value of null:
param1 = null
param2 = null
param3 = null
param4 = null
param5 = null
You may only want to specify a value for param4. But if you do, you also must at least specify param1, param2, and param3 in the call to the stored procedure.
That may not seem too much of an issue. But if you have many default params and only need to specify say the 15th parameter, it will be quite tedious to also have to specify the other 14.
At least that's not fatal. But it is fatal in combination with the following Hibernate rule: Hibernate requires that each specified param is not null. But what if the procedure requires that only param3 or param4 have a value (but not both)? If you specify param4 to have a value, then you must include param3 (per MS driver) and you must give it a value (per Hibernate). So, you're sunk.
The only solutions are
Use a different driver such as jTDS (which has an issue on truncating >8000 bytes) OR
Write a wrapper procedure to just have only the needed params (e.g. param4) and let it call the procedure with five parameters.
Details of the exception in the OP
The Microsoft SQL Server driver has found (in the db metadata) four stored procedure parameters and returns the name and index of each. Then, the Hibernate / JPA code is building the CallableStatement.Via #NamedStoredProcedureonly three parameters have been defined. So, it builds something like pTest(?,?,?). But then Hibernate uses the metadata to say that Date should be in the fourth position which is out of range. The built pTest only has three parameters.
I have MS SQL Server stored procedure (SQL Server 2012) which returns:
return value describing procedure execution result in general (successfull or not) with return #RetCode statement (RetCode is int type)
one result set (several records with 5 fields each)
another result set (several records with 3 fields each)
I calling this procedure from my Groovy (and Java) code using Java's CallableStatement object and I cannot find right way to handle all three outputs.
My last attempt is
CallableStatement proc = connection.prepareCall("{ ? = call Procedure_Name($param_1, $param_2)}")
proc.registerOutParameter(1, Types.INTEGER)
boolean result = proc.execute()
int returnValue = proc.getInt(1)
println(returnValue)
while(result){
ResultSet rs = proc.getResultSet()
println("rs")
result = proc.getMoreResults()
}
And now I get exception:
Output parameters have not yet been processed. Call getMoreResults()
I tried several approaches for some hours but didn't find correct one. Some others produced another exceptions.
Could you please help me with the issue?
Thanks In Advance!
Update (for Tim):
I see rc while I launched code:
Connection connection = dbGetter().connection
CallableStatement proc = connection.prepareCall("{ ? = call Procedure_Name($param_1, $param_2)}")
boolean result = proc.execute()
while(result){
ResultSet rs = proc.getResultSet()
println(rs)
result = proc.getMoreResults()
}
I see rc as object: net.sourceforge.jtds.jdbc.JtdsResultSet#1937bc8
I am passing an array to a PL/SQL package function. I am doing this to use this array in a query inside the function which has IN clause.
My declaration of package looks like :
create or replace
PACKAGE selected_pkg IS
TYPE NUM_ARRAY IS TABLE OF NUMBER;
FUNCTION get_selected_kml(
in_layer IN NUMBER,
in_id IN NUMBER,
in_feature_ids IN selected_pkg.NUM_ARRAY,
in_lx IN NUMBER,
in_ly IN NUMBER,
in_ux IN NUMBER,
in_uy IN NUMBER
)
RETURN CLOB;
END selected_pkg;
In my PL/SQL function I am firing a query like following
select a.id, a.geom from Table_FIELD a where a.id in (select * from table (in_feature_ids)) and sdo_filter(A.GEOM,mdsys.sdo_geometry(2003,4326,NULL,mdsys.sdo_elem_info_array(1,1003,3), mdsys.sdo_ordinate_array(0,57,2.8,59)),'querytype= window') ='TRUE'
The same query runs fine if I run it from anonymous block like
CREATE TYPE num_arr1 IS TABLE OF NUMBER;
declare
myarray num_arr1 := num_arr1(23466,13396,14596);
BEGIN
FOR i IN (select a.id, a.geom from Table_FIELD a where a.id in (select * from table (myarray)) and sdo_filter(A.GEOM,mdsys.sdo_geometry(2003,4326,NULL,mdsys.sdo_elem_info_array(1,1003,3), mdsys.sdo_ordinate_array(0,57,2.8,59)),'querytype= window') ='TRUE'
loop
dbms_output.put_line(i.id);
end loop;
end;
If I try to run it by calling function as below
--Running function from passing array for IDs
declare
result CLOB;
myarray selected_pkg.num_array := selected_pkg.num_array(23466,13396,14596);
begin
result:=SELECTED_PKG.get_selected_kml(3, 19, myarray, 0.0,57.0,2.8,59);
end;
I am getting error
ORA-00904: "IN_FEATURE_IDS": invalid identifier
Could someone please help me understand the cause of it?
Thanks,
Alan
You cannot query a type declared in plsql in a sql query, as the sql engine doesn't recognise it.
Your first example works because you have declared the type numarr1 in the database, whereas the type selected_pkg.num_array is declared in a package.
Good summary here
I can't quite recreate the error you're getting; the anonymous block doesn't refer to in_feature_ids, and the package ought to only report that if it doesn't recognise it on compilation rather than at runtime - unless you're using dynamic SQL. Without being able to see the function body I'm not sure how that's happening.
But you can't use a PL/SQL-defined type in an SQL statement. At some point the table(in_feature_ids) will error; I'm getting an ORA-21700 when I tried it, which is a new one for me, I'd expect ORA-22905. Whatever the error, you have to use a type defined at schema level, not within the package, so this will work (skipping the spatial stuff for brevity):
CREATE TYPE num_array IS TABLE OF NUMBER;
/
CREATE OR REPLACE PACKAGE selected_pkg IS
FUNCTION get_selected_kml(
in_layer IN NUMBER,
in_id IN NUMBER,
in_feature_ids IN NUM_ARRAY,
in_lx IN NUMBER,
in_ly IN NUMBER,
in_ux IN NUMBER,
in_uy IN NUMBER
) RETURN CLOB;
END selected_pkg;
/
CREATE OR REPLACE PACKAGE BODY selected_pkg IS
FUNCTION get_selected_kml(
in_layer IN NUMBER,
in_id IN NUMBER,
in_feature_ids IN NUM_ARRAY,
in_lx IN NUMBER,
in_ly IN NUMBER,
in_ux IN NUMBER,
in_uy IN NUMBER
) RETURN CLOB IS
BEGIN
FOR i IN (select * from table(in_feature_ids)) LOOP
DBMS_OUTPUT.PUT_LINE(i.column_value);
END LOOP;
RETURN null;
END get_selected_kml;
END selected_pkg;
/
... and calling that also using the schema-level type:
set serveroutput on
declare
result CLOB;
myarray num_array := num_array(23466,13396,14596);
begin
result:=SELECTED_PKG.get_selected_kml(3, 19, myarray, 0.0,57.0,2.8,59);
end;
/
23466
13396
14596
PL/SQL procedure successfully completed.
Also note that you have to use exactly the same type, not just one that looks the same, as discussed in a recent question. You wouldn't be able to call your function with a variable of num_arr1 type, for example; they look the same on the surface but to Oracle they are different and incompatible.
I would like to know if in SQL is it possible to return a varchar value from a stored procedure, most of the examples I have seen the return value is an int
Example within a proc
declare #ErrorMessage varchar(255) if #TestFlag = 0 set #ErrorMessage = 'Test'
return #ErrorMessage
calling on asp.net
Updated:
Error:
Conversion failed when converting the varchar value 'Development' to data type int.
using (DataContextDataContext dc = conn.GetContext())
{
string serverName = ""
var result = dc.spGetServerName(ref serverName);
return result.ToString();
}
No, you can't return anything but an int using the RETURN statement.
You'd need to use OUTPUT parameters instead. They are more flexible.
e.g.
CREATE PROCEDURE dbo.ExampleSproc
#OutParam VARCHAR(10) OUTPUT
AS
SET #OutParam = 'Test'
...
GO
Update:
For an example on how to retrieve the output param, see this article.
No. Please see: http://msdn.microsoft.com/en-us/library/ms174998.aspx