I am currently writing a Java web application which interfaces with an Oracle database. I am using PreparedStatements because Hibernate would complicate things too much.
Due to a bug in the program which is writing to the database, the field I need to search for has trailing spaces written to the value. I have surrounded the value with quotation marks to demonstrate the whitespace.
"testFTP_receipt521 "
When I do a select query with SQLDeveloper, I am able to get a result when I run:
...and yfs_organization.ORGANIZATION_KEY='testFTP_receipt521';
(no whitespace)
However, when I use a PreparedStatement, I get no results when I try:
...and yfs_organization.ORGANIZATION_KEY=?");
preparedStatement.setString(1, "testFTP_receipt521");
(no whitespace)
and when I try:
...and yfs_organization.ORGANIZATION_KEY=?");
preparedStatement.setString(1, "testFTP_receipt521 ");
(with whitespace)
Are there any ways that I can query for this result with a PreparedStatement, or should I try another approach?
Thanks for all your help.
Due to a bug in the program which is writing to the database, the field I need to search for has trailing spaces
Maybe, given the circumstances, and if your version of Oracle is recent enough, you might consider adding a virtual column to your table containing the correct value?
ALTER TABLE yfs_organization ADD (
ORGANIZATION_KEY_FIXED VARCHAR(80)
GENERATED ALWAYS AS (TRIM(ORGANIZATION_KEY)) VIRTUAL
);
Then in your code, the only change will be to use the ORGANIZATION_KEY_FIXED to query the DB:
SELECT ID,ORGANIZATION_KEY_FIXED
FROM yfs_organization
WHERE ORGANIZATION_KEY_FIXED='testFTP_receipt521'
(try it on http://sqlfiddle.com/#!4/8251d/1)
This might avoid to scatter around your application the code required to work around that bug. And might ease the transition once it will be fixed.
As an added benefice, you could add index on virtual columns if you need too.
Maybe you can use it like this...
...and yfs_organization.ORGANIZATION_KEY like '%testFTP_receipt521%';
this way returns you all reg where contains 'testFTP_receipt521' independently of whitespace.
Antoher thing that i saw in your code in this part
...and yfs_organization.ORGANIZATION_KEY=?");
preparedStatement.setString(1, "testFTP_receipt521");
i thing this is the correct way
...and yfs_organization.ORGANIZATION_KEY='?'");
you need to put quotes around the criteria
If you have the ability to modify the query, you can TRIM(...) the column value and perform the comparison. For example:
...and TRIM(yfs_organization.ORGANIZATION_KEY)=?");
Hope it helps.
Related
I have created the following query which is now in one of my java classes being used by Hibernate.
private static final String COUNT_INTERQUARTILE_SQL
= " SET #number_of_rows \\:= (SELECT COUNT(*) FROM carecube.visit)" +
" SET #quartile \\:= (ROUND(#number_of_rows*0.25))" +
" SET #medianquartile \\:= (ROUND(#number_of_rows*0.50))" +
" SET #sql_q1 \\:= (CONCAT('(SELECT 'Q1' AS quartile, visit.id FROM carecube.visit order by visit.id LIMIT 1 OFFSET ', #quartile, ')'))" +
" SET #sql \\:= (CONCAT_WS(' UNION ', #sql_q1, #sql_med))" +
" PREPARE stmt1 from #sql;" +
" EXECUTE stmt1;";`
The stack trace complains of a syntax errors for each line where I've set a mysql variable. Obviously it works in MySQL just fine.
I read that I can use double backslashes with assignments in Hibernate. This is the first time I've tried to use MySQL variables with Hibernate so am unsure if I'm missing anything out and whether 'PREPARE' and 'EXECUTE' are necessary?
Can someone with more knowledge point me where I am going wrong?
Also, where I am selecting Q1, I've placed that in single quotes, in MySQL workbench it is double quotes.
EDIT: I've added double quotes so hibernate doesn't throw a sissy fit with the assignments. I still can't for the life of me, figure out why I cannot just use '#sql' after i've prepared it.
EDIT: I receive the following error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'PREPARE stmt1 from #sql_max; EXECUTE stmt1' at line 1
Thanks
I'm not sure if this is possible, but in my opinion this doesn't make much sense.
Some people have asked similar questions (they have some code samples in the answers if you want to check):
How to use Mysql variables with Hibernate?
How can I use MySQL assign operator(:=) in hibernate native query?
Hibernate is an ORM (Object Relational Mapping), so it's supposed to convert data between incompatible type systems (data from database) in objects. As far as I could understand your query, you're returning a COUNT, so it would be just one single result (row) and one single column, is that right?
Due the complexity of your query, I would say there are some options I could think of:
Use Criteria or HQL to run each query using Hibernate and then in Java work with the logic to have your desired result (may become much slower than the query in MySQL)
Create a VIEW with this SELECT (if possible), map it into an object as an #Entity and query directly to it
Create a FUNCTION/PROCEDURE (this is possible) and call it using CallableStatement
I like to think that the two elements here (Hibernate x Database) should have a well define responsibility in the project. First of all, I would try to use only Criteria/HQL for the queries (to use object properties), but if needed to use SQL I would keep all queries ANSI to allow interoperability. If it's not possible, I would create an object in the database to return what I want (view/procedure/function).
Mixing specific database provider code in the query, like your example, doesn't look a good practice.
If possible, I would definitely go for option 2. If it can't be done, surely for number 3.
Hope it's somehow helpful.
I am having trouble puting those single quotes for ASCII/Timestamp columns and not puting for other types like Int, Decimal, Boolean etc.
The data comes from another db/table, which is a sql.
I have all the column data as string. I don't want to format each column data to check null values and then decide to put quote or not.
Is it possible to pass in insert data value without giving single quotes, using prepared statement or whatever.
If you don't want to write a loader that uses prepared statements (via the CQL driver...which is a good idea), I can think of one other way. To import without using single quotes, you should be able to accomplish this with the COPY FROM CQL3 command (setting the QUOTE parameter to an empty string). If you can dump your RDBMS data to a csv file, you should be able to insert those values into Cassandra like this:
COPY myColumnFamily (colname1,colname2,colname3)
FROM '/home/myUser/rdbmsdata.csv' WITH QUOTE='';
Check out the documentation on the COPY command for more information. Examples can be found here.
EDIT:
I also read the above question and assumed that you did not want a prepared statement-based answer. Since that's obviously not the case, I thought I'd also provide one here (using DataStax's Java CQL driver). Note that this answer is based on my column family and column names from my example above, and assumes that col1 is the (only) primary key.
PreparedStatement statement = session.prepare(
"UPDATE myKeyspace.myColumnFamily " +
"SET col2=?, col3=? " +
"WHERE col1=?");
BoundStatement boundStatement = statement.bind(
strCol2, strCol3, strCol1);
session.execute(boundStatement);
This solution does not require you to encapsulate your string data in single quotes, and has a few added benefits over your String.ReplaceAll:
Allows you to insert values containing single quotes.
Escapes your values, protecting you from CQL-Injection (the lesser-known relative of SQL-Injection).
In CQL, both UPDATE and INSERT add a record if it does not exist and update it if it does (effectively known as an "UPSERT"). Using an UPDATE over an INSERT supports counter columns (if your schema ends up using them).
Prepared statements are faster, because they allow Cassandra to only have to parse the query once, and then re-run that same query with different values.
For more information, check out DataStax's documentation on using prepared statements with the Java Driver.
Finally did it using String.format clubbed with replace
String.format("INSERT INTO xyz_zx(A,B,C,D) VALUES('%s','%s',%s,%s);",(Object[])Strings).replaceAll("'null'","null");
I am writing a web service that essentially allows users to submit queries to pre-existing tables in various SQL databases against advertised columns.
I have a PostgreSQL table defined like that:
CREATE TABLE stpg.test (
test integer,
"Test" integer,
"TEST" integer
);
insert into stpg.test values (1,2,3);
To determined the names of the available columns I run the following Java code:
ResultSet rs = dbmd.getColumns(null, "stpg", "test", null);
while (rs.next()) {
System.out.println(rs.getString("COLUMN_NAME"));
}
I get:
test
Test
TEST
If a user submits a query, referring to the columns as they were returned, like
select test, Test, TEST from stpg.test he will get 1 1 1 instead of expected 1 2 3.
Is this a bug?
I know that doing select test, "Test", "TEST" from stpg.testreturns results correctly. But my users would not know that to fetch the values of "capitalized" columns that were defined in quotes they need to use quotes in the query.
Is there a way I could could determine that a column name is case sensitive so that I could report its name in quotes? I need to do that generically against different databases, so JDBC api approach is preferable. I tried using ResultSetMetaData and invoking getColumnName and getColumnLabel but they return the names without the quotes. Calling isCaseSensitive always returns false.
Is there a way I could could determine that a column name is case sensitive so that I could report its name in quotes?
It looks like you are saying that a column name needs to be quoted if it contains any upper-case letters. In that case:
if (!name.equals(name.toLowercase())) {
// needs quoting.
}
But this is moot:
if you just quote all column names, or
if you treat user-supplied column names as case insensitive.
(On the latter point, having column names where case sensitivity matters is probably a bad design. Case sensitivity is certainly not something that you'd want your website users to have to worry about ...)
I tried using ResultSetMetaData and invoking getColumnName and getColumnLabel but they return the names without the quotes.
As they should! The quotes are not part of the column names! They are part of the (Postgres) SQL syntax for identifiers (in general). The name is the stuff within the quotes.
Calling isCaseSensitive always returns false.
To be honest, it is not entirely clear (from the javadoc) what the result of that method means. But it sounds like you might have found a bug in the JDBC driver you are using. (Or maybe you are just mistaken. The code for that implements that method in the current Postgres does consult the column type information ...)
I would suggest to always quote the column names. There is no real reason why you would remove the quotes. And, more importantly, the code to decide whether to quote or not is certainly going to span over 10-15ish lines with no added value. That's about 15 lines of code which can introduce new bugs, typos, conceptual errors.
Just quoting each column is straight-forward and always correct!
Also, regarding to your question if the result of select test, Test, TEST from stpg.test is a bug: It's not. It's the default behaviour of PostgreSQL. All column names (or, db-object names) are always lowered except if they are enclosed in quotes. This also leads us to isCaseSensitive. It is always false because it is not case-sensitive.
A more important note: If you let your users type in SQL queries, you will likely run into other weird problems. You will never know what kind of shenanigans your users type. Either by design or by accident ;)
If this is one of the first times you allow users to enter SQL queries, consider your plan of action carefully! Users type errors, mistakes (full-cartesian products on 5 tables with millions of rows? And only then apply filters?... fun times...), or might even try to play with your DB. If you decide on really doing this, buckle up! :) It all depends on the technical knowledge of you user-base.
Also, in Postgres I find it useful to keep everything lower-cased and user underscores to separate words. Like user_account instead of UserAccount.
I'm converting (or trying to) an Ms AccessDB into derby.
When I extract the data from certain varchar / text / memo field from access they are filled with apostrophe, and mathematical symbols (percent, less than etc), and possible foreign characters
I need to keep these and I test for them so as I can use an 'escape sequence' to ensure they get put into the database.
However for now I am unable to get the data into the DB without it failing on these fields. When the SQL fails I output the SQL string, and cut and past it into ij. Then I modify just the first record, and it is always these characters that cause me grief.
I've tried to modify the strings by surrounding with "double quote marks" but that just gives a different error (stating that it has 'enounterd """ at line1 column x' which is always the first occurance of the double quote).
I haven't found a setting in derby to alter the behaviour for strings, yet. Is there one?
I have also tried to set the SQL statment to a preparedStatement then use the {call preparedStatement} again this fails also. I can't use the {escape "escape char} in a normal statment as derby just says incorrect syntax at me.
How do others manage to get user content with strange characters into a field in derby?
Do I need to change my field into a CLOB or something other than varchar / long Varchar?
Are my problems being caused by using the wrong characteset (eg iso rather UTF-8), how do I tell what it is, how to change it?
Below is a sample of the SQL insert that fails when I send it to derby (via my JAVA 'programme')
insert into S1.SORTIEDESSAI (OBS, DATEDUSORTIE, CONTREINDIC, FIN,
PDEVU, REFUS, INVDECISN, ADMIN, MOTIF_DE_LA_SORTIE, NOMVALIDEE,
DATEVALIDEE) values ('"0001/0001"' , '2007-07-15' , false , true ,
'"null"' , '"null"' , '"null"' , '"null"' , '"2. FIN DE L’ESSAI"' ,
'"DR SIMON"' , '2011-04-19' )
Note:
Actually I look at the above and notice that the order of columns names isn't good? It was OK yesterday, not sure why it would have changed? something to do with Access returning the column names in a random order from the resultSetMetaData, which would be a surprise.
for now I recomend any further answers to hold off whilst I sort this problem out, OK solved that problem, do I need to set another question about this behaviour....
Back to the main thread...
Ok as you can see on my SQL statement I have wrapped any varchar fields in double quotes. This always fails (even directly through ij). help help help...
I'm not quite sure what your question is, but in general you can input these characters by using a PreparedStatement of the form: INSERT INTO tablename (columnname) values (?), and then using the PreparedStatement.setString() method to supply your character data for that column.
In my project I have to create insert statements based on the tables passed.
So I have two options
1)Write preparedstatement and run it in batch
2)Create insert into table values(..),(..),(..)
I want to know the reasons to prefer (1) over (2) if I use Matcher.quoteReplacement() to escape the values
Thanks in advance
Matcher.quoteReplacement isn't intended to quote SQL strings, so you can't be certain to avoid SQL insertions using it. But even if you had a working quoting function, prepared statements would still be better for several reasons:
You don't need to worry about forgetting to quote input
You're not tempted to take a shortcut and not quote values you "know" are safe
The database can cache the execution plan to avoid parsing similar SQL queries with different parameters, giving a performance boost
The code gets more readable (IMHO)