How can I preempt a "Specified cast is not valid" exception? - sql

In querying an MS Access database in a Web API app, I get a "Specified cast is not valid" exception if I'm trying to assign an empty string to a variable. IOW, when this code:
var accountID = oleDbD8aReader.GetInt16(0);
var platypusName = oleDbD8aReader.GetString(1);
...reaches a record where the second column in the result set contains an empty/null string, it bombs.
So, I thought I could head that off at the pass like this:
string platypusName;
var accountID = oleDbD8aReader.GetInt16(0);
if (!string.IsNullOrEmpty(oleDbD8aReader.GetString(1)))
{
platypusName = oleDbD8aReader.GetString(1);
}
else
{
platypusName = string.Empty;
}
...but it doesn't work - I still get a "Specified cast is not valid" exception.
How can I safely check for an empty string/null value there and ignore that pass through the result set so that it will get subsequent records?
Or can I preclude this by changing the SQL statement to exclude empty/null strings from the result set? If so, how? The query is in this format:
SELECT td_duckbill_accounts.platypus_no, t_accounts.name
FROM t_accounts
INNER JOIN td_duckbill_accounts ON t_accounts.account_no = td_duckbill_accounts.account_no
ORDER BY td_duckbill_accounts.platypus_no

I think having the query return empty string is easy solution:
SELECT td_duckbill_accounts.platypus_no,
IIF(ISNULL(t_accounts.name),'',t_accounts.name) AS name
FROM t_accounts
INNER JOIN td_duckbill_accounts ON t_accounts.account_no = td_duckbill_accounts.account_no
ORDER BY td_duckbill_accounts.platypus_no
This should also work but I can't test it right now:
SELECT td_duckbill_accounts.platypus_no,
Nz(t_accounts.name,'') AS name
FROM t_accounts
INNER JOIN td_duckbill_accounts ON t_accounts.account_no = td_duckbill_accounts.account_no
ORDER BY td_duckbill_accounts.platypus_no

Sometimes what is needed is to change the data type method used in OleDbDataReader, as this code and the comments show:
while (oleDbD8aReader != null && oleDbD8aReader.Read())
{
string accountId = oleDbD8aReader.GetString(0);
string accountName = oleDbD8aReader.GetString(1);
//int useOnItems = oleDbD8aReader.GetInt32(2); <= get "specified cast not valid" with Int32
int useOnItems = oleDbD8aReader.GetInt16(2); // This works
expenses.Add(new Expense { account_id = accountId, name = accountName, use_on_items = useOnItems });
}
The data type in the underlying table is Integer, so I guess for LongInteger (another Access integer type) GetInt32() would be the OleDbDataReader method to use.

Related

SQLSTATE[22007]: Invalid datetime forma

I try to save some data that it brings me from my view, which is a table, but I don't know why it throws me that error with the insert.
result of insert
this is my view:
table of view
this is my controller:
$checked_array = $_POST['id_version'];
foreach ($request['id_version'] as $key => $value) {
if (in_array($request['id_version'][$key], $checked_array))
{
$soft_instal = new Software_instalacion;
$soft_instal->id_instalacion = $instalaciones->id;
$soft_instal->id_historial = $historial->id;
$soft_instal->id_usuario = $request->id_usuario;
$soft_instal->id_version = $_POST['id_version'][$key];
$soft_instal->obs_software = $_POST['obs_software'][$key];
$soft_instal->id_tipo_venta = $_POST['id_tipo_venta'][$key];
$soft_instal->save();
}
}
id_tipo_venta seems to be an empty string which is apparently not valid.
You can try debugging what you get in :
var_dump($_POST['id_tipo_venta'][$key]);
die;
Your database field expects to receive an integer. Therefore, using the intval() function can solve your problem.
Indeed, I think your code returns an alphanumeric string.
Therefore, the code below will return 0 in all cases if no version is returned (not set, string or simply null):
$soft_instal->id_tipo_venta = intval($_POST['id_tipo_venta'][$key]);
On the other hand, intval() will always convert to int, so a decimal will be converted, example :
intval("1.1") // returns 1
intval("v1.1") // returns 0
If this is not the desired behavior, maybe you should think about changing your database type.
EDIT :
Of course, you can also set the value as null if you prefer to 0. You must allow nullable values in your database.
id_tipo_venta can not be empty, try with some number or change type column to varchar in the database

How to set large string as param without getting ORA-01460: unimplemented or unreasonable conversion error?

In spring-boot using namedParameterJdbcTemplate (Oracle db version 12 and odbc8 driver 12.2)
I am getting the following error while executing a SELECT query bound with a parameter larger than 4000 character whereas update queries working fine.
ORA-01460: unimplemented or unreasonable conversion requested
The unit test I am trying to execute;
#Test
public void testSqlSelectQueryLargeStringParameter() {
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("param", theLargeStr);
// #formatter:off
String sql =
"SELECT NULL id, NULL text FROM DUAL WHERE 'X' != :param ";
// #formatter:on
namedParameterJdbcTemplate.queryForRowSet(sql, params);
}
Is there any way to set this large param via MapSqlParameterSource?
I am #ahmet-orhan 's colleague, we've found a solution.
Thanks #kfinity for your suggestion, It is working for insert and update but we are still getting this error when we set clob or blob as "paremeter" in select statements.
If using a driver that supports JDBC4.0, the right solution is create a DefaultLobHandler and set streamAsLob or createTemporaryLob to true.
MapSqlParameterSource params = new MapSqlParameterSource();
String myString = "";
for (int i = 0; i < MAX_CLOB_BLOB_SIZE_IN_SELECT; i++) {
myString = myString + "1";
}
DefaultLobHandler lobHandler = new DefaultLobHandler();
lobHandler.setStreamAsLob(true);
params.addValue("param", new SqlLobValue(myString, lobHandler), Types.CLOB);
// #formatter:off
String sql =
"SELECT 1 id FROM DUAL WHERE :param IS NOT NULL ";
// #formatter:on
Integer id = namedParameterJdbcTemplate.queryForObject(sql, params, Integer.class);
We prefer streamAsLob but to be honest we have no idea which one is better.
This comment points out that ORA-01460 in JDBC queries is the same as "ORA-01704: string literal too long". (You can't have string literals longer than 4000 characters.) Maybe try this solution?
params.addValue("param", theLargeStr, Types.CLOB);
Although also != won't work for clob comparison, so you'll also need to change your query to
SELECT NULL id, NULL text FROM DUAL WHERE dbms_lob.compare('X',:param) != 0

How to insert multiple integer parameters into query?

Website user can enter search criteria to query orders. User, States, Status, OrderID, etc.
Website communicates with API. Query parameters are in the header, so I assume they come in as strings. API communicates with Access via Dapper.
For some criteria, they can send multiple values. So I want to use an "IN" clause.
where UserID in (150, 3303, 16547)
Dapper handles this nicely.
connection.Query<int>("select * from table where Id in #Ids", new { Ids = new int[] { 1, 2, 3 } });
This works in MS-Access
SELECT top 100 * from Orders where UserID in (150, 30330)
But that only works when the values are ints. String and Strings both give "Data type mismatch in criteria expression" in Access.
SELECT top 100 * from Orders where UserID in ("150", "30330") // two strings
SELECT top 100 * from Orders where UserID in ("150, 30330") // single string
It may be a coincidence, but all the examples I see are integers. Access throws an error on strings if you don't specify the size. Using DynamicParameters makes it easy to specify the size.
But when the field is an int, my dapper code gives the same error (Data type mismatch in criteria expression):
var paramlist = new DynamicParameters();
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
paramlist.Add("userId", userId, DbType.String, ParameterDirection.Input, 50);
sbWhere.AppendFormat("AND CustFID in (?) ", paramIndex++);
}
So I assume the issue is that I'm telling it that the parameter is a string.
But if I make the parameter an int, then it won't take the string with multiple values. Conversely, if I include the () in the string, it complains about the parens being missing from the 'in' clause.
I tried splitting the string of numbers into an array and/or list.
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
var userIds = userId.Split(','); //.ToList(); fails, can't map to native type
paramlist.Add("userId", userIds, DbType.String, ParameterDirection.Input, 1000);
if (userIds.Length > 1) {
sbWhere.AppendFormat("AND CustFID in #userId ", paramIndex++);
} else {
sbWhere.AppendFormat("AND CustFID = #userId ", paramIndex++);
}
}
and it gives ": No mapping exists from object type System.String[] to a known managed provider native type." whether I say the parameters are int32 or string.
UPDATE:
There may be multiple search criteria, so I'm using DynamicParameters.
Here is my attempt at implementing Palle Due's idea.
if ((!string.IsNullOrWhiteSpace(userId)) && userId != "0") {
// var userIds = userId.Split(',').Select(i => Int32.Parse(i)).ToList();// fails, can't map to native type
IEnumerable<int> userIds = userId.Split(',').Select<string, int>(int.Parse);
paramlist.Add("userId", userIds, DbType.Int32, ParameterDirection.Input);
if (userIds.Count() > 1) {
sbWhere.AppendFormat("AND CustFID in #userId ", paramIndex++);
} else {
sbWhere.AppendFormat("AND CustFID = #userId ", paramIndex++);
}
}
using (IDbConnection conn = Connection) {
string sQuery = string.Format("SELECT {0} FROM vwweb_Orders {1}", columns, where);
conn.Open();
var result = await conn.QueryAsync<Order>(sQuery, paramlist);
return result.ToList();
}
throws
Message: System.AggregateException : One or more errors occurred. (Failed to convert parameter value from a SelectArrayIterator`2 to a Int32.)
----> System.InvalidCastException : Failed to convert parameter value from a SelectArrayIterator`2 to a Int32.
----> System.InvalidCastException : Object must implement IConvertible.
The github page #Dai links to specifies that the Dapper list support only works with IEnumerable<int>.
But as I understand it your UserID is an int, so I don't get why you try to enter a string. You need to get the string the user has input and convert it to IEnumerable<int>. Do something like this:
IEnumerable<int> userIDs = (userId?? "").Split(',').Select<string, int>(int.Parse);
var result = connection.Query<int>("SELECT TOP 100 * FROM Orders WHERE UserID IN #Ids", new { Ids = userIDs });
You might want to apply some input checking to that, and you might also want to reconsider using Access as the "database" for a website. It's not what it was meant for.
I give up. Dapper should be able to handle this, but it's a newer feature, so...
I just built the IN clause myself.
if (userIds.Count() > 1) {
sbWhere.AppendFormat("AND CustFID in ( ");
int paramCnt = 0;
foreach (int id in userIds) {
sbWhere.AppendFormat("?, "); // Access doesn't mind a trailing ,
paramlist.Add("userId" + paramCnt.ToString(), id, DbType.Int32, ParameterDirection.Input);
paramCnt++;
}
sbWhere.AppendFormat(") ");
} else {
sbWhere.AppendFormat("AND CustFID = ? ");
paramlist.Add("userId", userIds.ToArray<int>()[0], DbType.Int32, ParameterDirection.Input);
}

DataContext.ExecuteQuery<object> returns object {}

I'm trying to write function for selecting optional columns in linq(columns that may not exist). The problem is in linq like this:
using (DataDataContext db = new DataDataContext()){
var collection = from t in table
select new
{
Nonoptional = t.A;
Optional = IsInDB("table","B") ? t.B : -1; //this is optional column
}}
Unfortunately, this won't work because the fragment near Optional will be translated to case statement and error arises that column not exists.
So i decided to "cover" it with function:
using (DataDataContext db = new DataDataContext()){
var collection = from t in table
select new
{
Nonoptional = t.A;
Optional = IsInDB("table","B") ? OptionalColumnValue<int>("table","B","id_table",t.id_table) : -1; //this is optional column
}}
I want this function to be universal. It should work like that" If there is no value or column is nullable and value is null then return default value for type.
I came up with something like this:
//table,column - obvious,id_column - PK column of table, id - id of currently processing record
public static T OptionalColumnValue<T>(string table,string column,string id_columm,int id) T t = default(T);
DataDataContext db = new DataDataContext();
IEnumerable<object> value = db.ExecuteQuery<object>("select " + column + " from " + table + " where " + id_columm + " = " + id.ToString());
List<object> valueList = value.ToList();
if (valueList.Count == 1)//here is the problem
t = (T)valueList.First();
return t;
}
When there is null value db.ExecuteQuery return something like object{}. I'm assuming this is "empty" object,with nothing really in there. I was thinking about checking for "emptiness" of this object( BTW this is not DBull).
When i realised that this is no way either with concrete value in this column(it cannot cast it to return correct type), then I tried db.ExecuteQuery<T>. Then concrete value - OK, null - Exception.
I thought, maybe Nullable<T> as return value. Nop, because string also can be T.
I don't know what to do next. Maybe there's another solution to this problem.

nhibernate hql with named parameter

I have implemented a search function using Castel Active Record. I thought the code is simple enough but I kept getting
NHibernate.QueryParameterException : could not locate named parameter [searchKeyWords]
errors. Can someone tell me what went wrong? Thanks a million.
public List<Seller> GetSellersWithEmail(string searchKeyWords)
{
if (string.IsNullOrEmpty(searchKeyWords))
{
return new List<Seller>();
}
string hql = #"select distinct s
from Seller s
where s.Deleted = false
and ( s.Email like '%:searchKeyWords%')";
SimpleQuery<Seller> q = new SimpleQuery<Seller>(hql);
q.SetParameter("searchKeyWords", searchKeyWords);
return q.Execute().ToList();
}
Why do not u pass the % character with parameter?
string hql = #"select distinct s
from Seller s
where s.Deleted = false
and ( s.Email like :searchKeyWords)";
SimpleQuery<Seller> q = new SimpleQuery<Seller>(hql);
q.SetParameter("searchKeyWords", "%"+searchKeyWords+"%");
return q.Execute().ToList();