Set ANORM SELECT parameter to null - scala

I'm trying to execute SELECT statement, where parameters might have null values:
SQL(
"""
SELECT id FROM devices WHERE
name = {name}
""")
.on("name" -> device.name)()
.collectFirst {
...
}.getOrElse {
...
}
device.name can return null. With db.default.logStatements=true I see that generated SQL looks like this: SELECT id FROM devices WHERE name = NULL.
name = NULL is not quite valid for Postgre SQL, but I've enabled it using transform_null_equals. Now when I execute SQL from log using pgAdmin, it works perfectly fine. However, ANORM does not find anything.
Following code does return result:
SQL(
"""
SELECT id FROM devices WHERE
name = NULL
""")
.on("name" -> device.name)()
.collectFirst {
...
}.getOrElse {
...
}
What's wrong with it?!

Related

SQLC named parameter in generated code for sql containing conditional where

I am using golang SQLC to generate CRUD operation go code from sql.
My select query is like bellow
-- name: SearchProducts :many
SELECT * FROM product
WHERE ( $1::varchar(100) IS NULL OR name LIKE '%$1%' )
AND ( $2::varchar(1000) IS NULL OR description LIKE '%$2%' );
SQLC generating code like bellow
type SearchProductsParams struct {
Column1 string `json:"column_1"`
Column2 string `json:"column_2"`
}
func (q *Queries) SearchProducts(ctx context.Context, arg SearchProductsParams) ([]Product, error) {
rows, err := q.db.QueryContext(ctx, searchProducts, arg.Column1, arg.Column2)
if err != nil {
return nil, err
}
....
Is there any way to configure sqlc so that it will use name & description instead of Column1 & Column2 in SearchProductsParams struct
You can use named parameters
-- name: SearchProducts :many
SELECT * FROM product
WHERE ( sqlc.arg(email)::varchar(100) IS NULL OR name LIKE '%sqlc.arg(email)%' )
AND ( sqlc.arg(description)::varchar(1000) IS NULL OR description LIKE '%sqlc.arg(description)%' );
You can find the documentation here
When doing selects with conditional filters what it works for me was something like this:
SELECT * FROM product
WHERE
email like CASE
WHEN #filter_email::bool
THEN #email::VARCHAR
ELSE '%'
END
AND
description = CASE
WHEN #filter_description::bool
THEN #description::VARCHAR
ELSE description
END
You have to use the flag filter_email or filter_description flag and the value when you actually want to filter.

Do "$1" parameters in an ilike expression need to be escaped when used with the postgres crate's query function?

I'm using the postgres crate which makes a query with postgres::Connection. I query a table based on a string value in an ilike '%search_string%' expression:
extern crate postgres;
use std::error::Error;
//DB Create queries
/*
CREATE TABLE TestTable (
Id SERIAL primary key,
_Text varchar(50000) NOT NULL
);
insert into TestTable (_Text) values ('test1');
insert into TestTable (_Text) values ('test1');
*/
fn main() -> Result<(), Box<dyn Error>> {
let conn = postgres::Connection::connect(
"postgres://postgres:postgres#localhost:5432/notes_server",
postgres::TlsMode::None,
)?;
let text = "test";
// //Does not work
// let query = &conn.query(
// "
// select * from TestTable where _text ilike '%$1%'
// ",
// &[&text],
// )?;
//Works fine
let query = &conn.query(
"
select * from TestTable where Id = $1
",
&[&1],
)?;
println!("Rows returned: {}", query.iter().count());
Ok(())
}
If I uncomment the //Does not work part of the code, I will get the following error:
thread 'main' panicked at 'expected 0 parameters but got 1'
It appears it doesn't recognize the $1 parameter that is contained in the ilike expression. I've tried escaping the single quotes and that doesn't change it.
The only dependencies are:
postgres = { version = "0.15.2", features = ["with-chrono"] }
To my surprise, here was the fix:
let text = "%test%";
let query = &conn.query(
"
select * from TestTable where _text like $1
",&[&text],
)?;
Apparently the postgres function knows to add single quotes around strings in this scenario.
I found out about this from here: https://www.reddit.com/r/rust/comments/8ltad7/horrible_quote_escaping_conundrum_any_ideas_on/
An example should do the magic.
At the top, I am using pg. So, import it. My code looks like
const title = req.params.title;
const posts = await db.query("SELECT * FROM postsTable INNER JOIN usersTable ON postsTable.author = usersTable.username WHERE title ILIKE $1 ORDER BY postsTable.created_on DESC LIMIT 5;", [`%${title}℅`])

Insert element of UUID type in nested array of Object through Postgresql query

Current JSON
{
"layout":"dynamicReport1",
"templateType":"DYNAMIC_REPORTS",
"containers":{
"fieldsContainer":[
{
"fieldName":"role_id",
"displayName":"Role",
"fieldType":"text",
"isHidden":true,
"index":0,
"queryForParam":"select name as \"role_id\" from um_role_master where id=#role_id#",
"queryIdForParam":476
},
{
"fieldName":"course_id",
"displayName":"Course",
"fieldType":"text",
"isHidden":true,
"index":1,
"queryForParam":"select course_name as course_id from tr_course_master where course_id=#course_id#",
"queryIdForParam":477
},
{
"fieldName":"location_id",
"displayName":"Location",
"fieldType":"text",
"isHidden":true,
"index":2,
"queryForParam":"select name as location_id from location_master where id = #location_id#",
"queryIdForParam":478
}
]
}
}
Hierarchy is like
containers -> fieldContainer -> object
Above is my json config and i want to add queryUUIDForParam: random UUID to each Object through query.
How i can insert ?
I tried to get updated config by this query but it throws error:
select config::jsonb || ('{"queryUUIDForParam":' || cast(uuid as text) || '}')::jsonb
error :
SQL Error [22P02]: ERROR: invalid input syntax for type json Detail:
Token "5574ff23" is invalid. Where: JSON data, line 1:
{"queryUUIDForParam":5574ff23...
My expected output is to add "queryUUIDForParam" element in Object Like.
I want an element with UUID value to be appended.This UUId value is generated using random function.
{
"layout":"dynamicReport1",
"templateType":"DYNAMIC_REPORTS",
"containers":{
"fieldsContainer":[
{
"fieldName":"role_id",
"displayName":"Role",
"fieldType":"text",
"isHidden":true,
"index":0,
"queryForParam":"select name as \"role_id\" from um_role_master where id=#role_id#",
"queryIdForParam":476,
"queryUUIDForParam":"1ea99f17-6965-4a0d-8d31-22b8777b9c62"
},
{
"fieldName":"course_id",
"displayName":"Course",
"fieldType":"text",
"isHidden":true,
"index":1,
"queryForParam":"select course_name as course_id from tr_course_master where course_id=#course_id#",
"queryIdForParam":477,
"queryUUIDForParam":"3ea99f17-6965-4a0d-8d31-22b8777b9c62"
},
{
"fieldName":"location_id",
"displayName":"Location",
"fieldType":"text",
"isHidden":true,
"index":2,
"queryForParam":"select name as location_id from location_master where id = #location_id#",
"queryIdForParam":478,
"queryUUIDForParam":"9ea99f17-6965-4a0d-8d31-22b8777b9c62"
}
]
}
}
Thanks in advance :)
Try This:
with cte as (select jsonb_array_elements(jsonb_extract_path(config, 'containers','fieldsContainer')::jsonb) "objects" from example),
final_array as (
select jsonb_build_array(d) "array_data" from (select array_agg(objects::jsonb || jsonb_build_object('queryUUIDForParam',(select uuid_generate_v4()))) "fieldsContainer" from cte )d)
select jsonb_set(
config::jsonb,
'{containers,fieldsContainer}', (f.array_data),false)
from example, final_array f;
in case you want different uuid for each object
with cte as (select uuid_generate_v4() "uuid_",jsonb_array_elements(jsonb_extract_path(config, 'containers','fieldsContainer')::jsonb) "objects" from example),
final_array as (
select jsonb_build_array(d) "array_data" from (select array_agg(objects::jsonb || jsonb_build_object('queryUUIDForParam',uuid_)) "fieldsContainer" from cte )d)
select jsonb_set(
config::jsonb,
'{containers,fieldsContainer}', (f.array_data),false)
from example, final_array f;
Note: I have used Inbuilt function of Postgres to generate the UUID. Please run following statement before using it
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
DEMO on DB-Fiddle

MyBatis Inner query

In my code base, we have a MyBatis query like below,
INSERT INTO TABLE_A(
ID,
NAME,
VERSION
)
VALUES (
#id#,
#name#,
(select NVL(max(version), 0 ) + 1 from SAE_PROC_SETTING where name = #name#)
)
The unique constraint is on columns name and version. If multiple users are inserting values at same time, sometimes we are getting unique constraint error. When we checked, the name and version is coming as same and throwing the error as expected.
My question is, how MyBatis handles the inner query
(select NVL(max(version), 0 ) + 1 from TABLE_Awhere name = #name#)
Does this query is submitted by MyBatis to Oracle and then oracle first executes the SELECT and then INSERT query OR Does MyBatis executes the SELECT first, then substitute the value in INSERT and submits the INSERT query in Oracle (2 steps by MyBatis) ?
If it's handled like second option, when 2 people are inserting with same name at same TIME, there is a probability that both got the same version. Then when trying to insert, we will get UniqueConstraint error.
Please let me know how MyBatis handles this internally. Any pointers are fine.
Thanks,
SD
MyBatis sends the full statement (insert with inner select) to JDBC driver and is the database (in your case Oracle) which does this operation (first executes the SELECT and then INSERT). MyBatis just replaces your query params and then passes the query by a prepareStatement.
The log show the insert as one statement.
2016-07-26 17:34:23.776 DEBUG 91036 --- [ main] sample.mybatis.mapper.CityMapper.insert : ==> Preparing: insert into city (name, state, country) values ((select DISTINCT 'a' from city), 'CA', 'US');
2016-07-26 17:34:23.777 DEBUG 91036 --- [ main] sample.mybatis.mapper.CityMapper.insert : ==> Parameters:
2016-07-26 17:34:23.779 DEBUG 91036 --- [ main] sample.mybatis.mapper.CityMapper.insert : <== Updates: 1
Then I debug it and when It is called this method (source github) just pass for INSERT case:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
.....
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
....
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
....
}

How can I limit my query by date using ScalaQuery

I'm using Scalaquery and have run into a problem when I attempt to limit my query based on a date field. I'm using Scala 2.9.2, ScalaQuery 2.9.1:0.10.0-M1 Consider the code below:
case class MyCase (opr_date: Date)
object MyClass extends BasicTable[MyCase]("MYTABLE") {
def opr_date = column[Date]("OPR_DATE")
def * = opr_date <> (MyCase, MyCase.unapply _)
def test(date: Date) = db.withSession {
logDebug("test date: " + date)
val qry = for {
d <- MyClass if (d.opr_date === date)
} yield d.opr_date
logDebug(qry.selectStatement)
qry.list
}
}
This query never returns any rows. Here is the calling code:
"The data" should {
"be available " in {
val testDate = CommonFormat.parseDate("2012-10-27", CommonFormat.EURO_SHORT).getTime
val records = MyClass.test2(new java.sql.Date(testDate))
records.size must be_>(0)
}
}
The query returns 0 rows and produces the following SQL when I print the select:
SELECT "t1"."OPR_DATE" FROM "MYTABLE" "t1" WHERE ("t1"."OPR_DATE"={d '2012-10-27'})
I have data available for the test date. If I paste the SQL into a SQL editor and edit the date so that its not the JDBC template format ('27-Oct-2012') the query returns the expected rows. Can anyone tell me what I'm doing wrong? Shouldn't this work?
I found out this morning that this was a data problem. The query works fine. It turns out I was connecting to the wrong server. We have a confusing setup of multiple environments and back-up systems that share the same database name. After connecting to the correct server the query works as expected. I saw different results between my code and the editor-tool because they were pointing at different servers (same database name ugh).Thank you to all who took time to look into this for me. I appreciate your efforts.