jdbi version 3, stringtemplate when to escape <, > characters? - java

I am using jdbi3 with StringTemplate 4 templating engine, I have this test query:
#SqlQuery("select * from test "
+ "where field1 = 5"
+ "<if(cond1)> or field2 \\<= :value1<endif>"
+ "<if(cond2)> or field2 >= :value2<endif>"
+ "<if(cond3)> or field2 in (<values>)<endif>")
#RegisterBeanMapper(Test.class)
#UseStringTemplateEngine
public List<Test> selectTest(
#Define("cond1") boolean cond1, #Bind("value1") int value2,
#Define("cond2") boolean cond2, #Bind("value2") int value3,
#Define("cond3") boolean cond3,
#BindList(value="values", onEmpty=BindList.EmptyHandling.NULL_STRING ) List<Integer> values);
Using StringTemplate engine when I have to escape with \ the characters < or > in the query?
Testing I found that I have to escape <= in the query like I did.
In the IN clause using #BindList I have to use the <values> but in this case I was expecting to escape it like \\<values> otherwise it will be used as attribute by StringTemplate but if I do this the query doesn't work.
About >= escaping it or not seems the same in the query.

Introduction
Let's consider:
The 3.27.1 Jdbi version as the current Jdbi version.
The 4.3.1 StringTemplate version as the current StringTemplate version, since the current Jdbi version uses it. Please, see: jdbi/pom.xml at v3.27.1 · jdbi/jdbi.
Answer
Jdbi
Documentation: Which characters to escape
Please, note the warning on which characters to escape:
Since StringTemplate by default uses the < character to mark ST expressions, you might need to escape some SQL: String datePredSql = "<if(datePredicate)> <dateColumn> \\< :dateFilter <endif>"
— jdbi/index.adoc at v3.27.1 · jdbi/jdbi.
Unit-test: Do not escape #BindList variable name
Please, see the BindListTest.ifValueGivenWithNullValueOptionThenResultIsTruthy() test method: jdbi/BindListTest.java at v3.27.1 · jdbi/jdbi.
Please, note that the test covers a very similar annotated method:
#SqlQuery("select name from something <if(name)> where name in (<name>) <endif>")
#UseStringTemplateEngine
List<String> getForValue(#Nonnull #BindList(value = "name", onEmpty = NULL_VALUE) List<String> name);
Please, note that the #BindList variable name is not escaped:
in (<name>)
StringTemplate
Documentation: Which characters to escape
Please, note which characters to escape:
A template is a sequence of text and expression elements, optionally interspersed with comments. At the coarsest level, the basic elements are:
text
<expr>
<! comment !>
Escape delimiters with a backslash character: \< or \>.
— stringtemplate4/templates.md at 4.3.1 · antlr/stringtemplate4.

Related

Get list of parameter names from native sql expression (regex)

I'm having trouble getting list of all parameters in SQL query using Regex.
Example of the query:
SELECT ... WHERE col1 = :user AND col2 = 'HELLO' OR col3 = :language
To obtain parameters, I use following regex pattern:
Pattern.compile(":([\\w.$]+|\"[^\"]+\"|'[^']+')", Pattern.MULTILINE)
The pattern returns list of parameters correctly:
:user
:language
The problem is with another type of query, where literals might contain character ':'
WHERE col1 = :user AND some_date > '2022-09-26T10:22:55'
The list of parameters for this case is:
:user
:22
:55
Is there any better approach that will not consider contents of literals as parameters?
You could simplify your problem by assuming that a named param in sql is just a word with prefix : and always follows after a space (this is actually not a requirement or always true but might be just good enough to get you acceptable results with as simple of regex as possible)
Pattern.compile(" :\\w+", Pattern.MULTILINE)
--
summary of the comments:
had to match
- foo = :param AND :param = bar AND foo=:param AND :param=bar
- AND FUNC(:param) OR FUNC(0, :param) OR FUNC(:param, 0)
finally this regex with fixed length lookahead and variable length lookbehind was helpful:
Pattern.compile("(?<=[=(])\\s*:[\\w_.]+|:[\\w_.]+(?=\s*[=)])", Pattern.MULTILINE)

restricting the regular expression only for a line

I have a CSV file below from one of the system.
""demo"",""kkkk""
""demo " ","fg"
" " demo" "
"demo"
"value1","" frg" ","vaue5"
"val3",""tttyy " ",""hjhj","ghuy"
Objective is get all the 2 pair double quotes removed and only one set of double quote is allowed like below. The spaces between the sets of double quote is not a fixed value. This has to be handled in a Java program using replaceAll
function in Java
"demo","kkkk"
"demo","fg"
"demo"
"demo"
"value1","frg","vaue5"
"val3","tttyy","hjhj","ghuy"
I tired this on regex101 with "[ ]*" and it works for PHP>=7.3 version but not in Java.
Also tried [\"][\"]|[^\"]\s+[\"] but still not getting desired output. Any suggestion please for the regular expression which can be used in Java program?
Based on shown sample data, you can use:
String repl = str.replaceAll("(?:\\h*\"){2}\\h*", "\"");
RegEx Demo
RegEx Details:
(?:\h*\"){2}: Match a pair of double quotes that have 0 or more whitespaces between them
\h*: Match 0 or more whitespace
Replacement is just a "

How to use apostrophe in formatted xpath?

I have place the locator in properties file like :
header.navigation.product.link = //div[contains(#class,'grid-')]//li/a[contains(.,'%s')]
and while I'm using this locator in my code-
String headerproductlink = String.format(ConfigurationManager.getBundle()
.getString("header.navigation.category.link"), category)
And category = Women's Gym Clothing
While I'm trying to locate the element it unable to find.
even i have tried as Women\'s Gym Clothing but no success.
Can someone please suggest a way ?
In XPath 1.0 you can use either single quotes or double quotes to delimit a string literal, and you can use the other kinds of quotes to represent itself within the string. You can't have a string literal containing both single and double quotes, but you can use concat() to get around this limitation:
concat('He said: "', "I won't", '"')
The situation is complicated if the XPath expression appears within a host language that imposes its own constraints; in that case any quotes within the XPath expression must be escaped using host language conventions, for example \" in Java, " in XML.
Below different ways worked for me:
Locator in Property file:
another.header=xpath=//h1[contains(.,"%s")]
Java code:
String t = "st. john\'s bay - women";
String header = String.format(getBundle().getString("another.header"), t);
CommonStep.get("https://www.jcpenney.com/g/st-johns-bay-women/N-bwo3xZ1z0nvauZ1z0nh7w");
String headerText=ElementFactory.$(header).getText();
Below also worked fine
Locator in Property file:
another.header={'locator':'xpath=//h1[contains(.,"%s")]'}
Java code:
String t = "st. john\\'s bay - women";
...
Or
Locator in Property file:
another.header={"locator":"xpath=//h1[contains(.,\\"%s\\")]"}
Java code:
String t = "st. john's bay - women";
...

Match string with normal characters and special characters in Spring

I'm trying to find a way to match user search queries with a database records in a search engine, using Spring, but I'm having trouble when the search query includes special characters such as vowels with accent.
Eg: search query = 'cafe'. Database record = 'café'
I'm using the stem of words to the query with the database records.
Which would be the most straight forward way of matching the query including a special character 'café' with the string that doesn't contain this special character 'cafe' and viceversa?
UPDATE
All the information I need is already cached so the approach of creating a new column in the db is not so appealing. I'm looking for a solution more spring based.
You could use java.text.Normalizer, like follow:
import java.text.Normalizer;
import java.text.Normalizer.Form;
public static String removeAccents(String text) {
return text == null ? null :
Normalizer.normalize(text, Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}
The Normalizer splits the original characters into a set of two character (letter and accent).
For example the character á (U+00E1) will be split in a (U+0061) and acute accent U+0301
The \p{InCombiningDiacriticalMarks}+ regular expression will match all such diacritic codes and we will replace them with an empty string.
And your query could be like:
SQL SERVER
SELECT * FROM Table
WHERE Column Like '%stringwithoutaccents%' COLLATE Latin1_general_CI_AI
ORACLE (from 10g)
SELECT * FROM Table
WHERE NLSSORT(Column, 'NLS_SORT = Latin_AI')
Like NLSSORT('%stringwithoutaccents%', 'NLS_SORT = Latin_AI')
The CI stands for "Case Insensitive" and AI for "Accent Insensitive".
I hope it helps you.

How to replace particular string in JAVA?

I have string like
order by o desc,b asc
Here I want to replace o and b columns of this clause by table_o and table_b and output
order by table_o desc, table_b asc
I am using replace function for that but output becomes like
table_order table_by table_o desc,table_b asc
How to solve this problem using regular expression?
One more example
"order by orders desc, bye asc"
should be replaced as
"order by table_orders desc, table_bye asc"
Here is one possible solution. [You might have to tweak spaces around desc asc and , based on your actual SQL]
String str = "select a,b,c * from Table order by o desc,b asc,c,d";
System.out.println(str.replaceAll(
"(.*order by )?(\\w+)( desc| asc)?(,|$)", "$1table_$2$3$4"));
Result
select a,b,c * from Table order by table_o desc,table_b asc,table_c,table_d
Visual Regex
Regex details
(.*order by)? => will match select a,b,c * from Table order by =>back ref $1
(\\w+) => will match column name =>back ref $2
( desc| asc)? => will match desc or asc => back ref $3
(,|$) => will match trailing comma or endof line => back ref $4
Please Note : this solution only works with simple sql queries, and would produce wrong result if the order byclause is part of inner query of a complex SQL. Moreover Regex is not can not ideal tool to parse SQL syntax
See this link Regular expression to match common SQL syntax?
If full-fledged SQL parsing is required, Its better to use either SQL parsers or Parser generators like ANTLR to parse SQL. See this link for list of available ANTLR SQL grammer
If you just want to replace text like that just use these regexes:
" o "
" b "
Probably you are looking for this? Regular Expressions in Java SE & EE Have a look at Regular Expressions chapter that will do the work most of the times.
Simply use a space in the replace function (you do not need a regex).
Pseudo-code:
string = string_replace(string, " o ", " table_o ")
Edit:
After your example, you can but every valid boundary between [ and ]. The regex will then match is. To get back the origional boundary put it between ( and ) and replace it back.
E.g.:
string = regex_replace(string, "([ \t])o([ \t,])", "\1o\2")
\1 and \2 might be different in your regex implementation.
Also I'd suggest clarifying your case so that it is clear what you really want to replace and also take a look at Truth's suggestion of the XY problem.
You can use code like this to convert your text:
String sql = "select o, b, c,d form Table order by orders ,b asc, c desc,d desc, e";
String text = sql.toLowerCase();
String orderBy = "order by ";
int start = text.indexOf(orderBy);
if (start >= 0) {
String subtext = text.substring(start+orderBy.length());
System.out.printf("Replaceed: [%s%s%s]%n", text.substring(0, start), orderBy, subtext.replaceAll("(\\w+)(\\s+(?:asc|desc)?,?\\s*)?", "table_$1$2"));
}
OUTPUT:
Replaceed: [select o, b, c,d form table order by table_orders ,table_b asc, table_c desc,table_d desc, table_e]

Categories