I use the following query to retrieve data from a .mdb file through JDBC, however when I try it on an .odb file it goes does not throw any exceptions but there are no results at all. I am wondering is .odb case sensitive where .mdb is not or is there something else I am missing?
"SELECT DISTINCT column-one + ':' + column-two As ResultColumn FROM datatable;"
How can I go about creating one statement that will work on both these file types?
They would be differnt because they are two differnt products written by two differnt companies and the programmers made different choices as to how to handle things.
Have you tried using a column alias you specify, perhaps something more descriptive than Expr1000?
SELECT DISTINCT column-one + ":" + column-two As Expr1000 FROM datatable
That's how I would write it in SQL Server, check your database to see if this would work.
Some suggestions:
1) Try using single quotes in place of double quotes e.g.
SELECT DISTINCT column-one + ':' + column-two As ResultColumn FROM datatable;
2) Perhaps the .odb source's SQL syntax handles concatenation differently e.g. with the .mdb the '+' NULLs will propagate meaning that if at least one the the column's values is NULL then the result will be NULL; the '&' concatenation symbol will ignore NULL values e.g. this in .mdb land
SELECT DISTINCT column-one & ':' & column-two As ResultColumn FROM datatable;
is equivalent to this in Standard ANSI/ISO SQL-92 (which isn't supported in .mdb land)
SELECT DISTINCT COALESCE(column-one, '') + ':' + COALESCE(column-two, '') As ResultColumn FROM datatable;
3) If the two sources do not support the same syntax, can you use the .mdb's linked table functionality to link the tables(s) from the .odb source in the .mdb and only use the SQL code in the .mdb?
Related
I am trying to setup a unit test environment for a large codebase. For these unit tests I use H2 database instead of the IBM DB2 database, which is used in production and I already implemented a few UDF's to map IBM DB2 related functions to the H2 database.
Some more Details about the project:
Java 8 JDK 321 64 Bit
DB2 12, DSN 12015
H2 version 2.1.212 with <MODE=DB2;DEFAULT_NULL_ORDERING=HIGH;
I am trying to implement the IBM Db2 function: strip() Reference Doc - IBM. This function is called as part of a larger select statement with the clause: STRIP(T2.ITEM_TYPE_NAME_GER, B, ' '). While I can map the first and last input parameter to a Java function and call this function as an ALIAS in H2, I was not able to manage to get the 2nd parameter interpreted in the correct way as a String or Expression. The JDBC/H2 engine always tries to map it to a table column:
org.h2.jdbc.JdbcSQLSyntaxErrorException: Feld "B" nicht gefunden
Column "B" not found; SQL statement:
SELECT T1.ITEM_TYPE_KEY,T1.SUPER_ITM_TYPE_KEY,T2.ITM_TYPE_KEY_TRANS,T2.ITEM_TYPE_NAME,T2.COMPLEX_FLAG,T2.ITEM_CATEGORY,T2.HEADER_FLAG,T2.HEADER_NO ,T2.LEVEL_NO,strip(T2.ITEM_TYPE_NAME_GER, B, ' ') ,T2.LEVEL1_DISPLAY FROM public.AA752T T1, public.AA743T T2 WHERE T1.ITEM_TYPE_KEY NOT IN ('F4CO', 'F4CB', 'F4RB', 'F4SO', 'F4SB', 'F4RO') AND T1.ITEM_TYPE_KEY = T2.ITEM_TYPE_KEY ORDER BY T2.HEADER_NO,T2.HEADER_FLAG DESC,T2.LEVEL_NO,T1.SUPER_ITM_TYPE_KEY,T2.LEVEL_PRIORITY [42122-212]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:502)
at org.h2.message.DbException.getJdbcSQLException(DbException.java:477)
at org.h2.message.DbException.get(DbException.java:223)
at org.h2.message.DbException.get(DbException.java:199)
at org.h2.expression.ExpressionColumn.getColumnException(ExpressionColumn.java:244)
at org.h2.expression.ExpressionColumn.optimizeOther(ExpressionColumn.java:226)
at org.h2.expression.ExpressionColumn.optimize(ExpressionColumn.java:213)
at org.h2.expression.function.JavaFunction.optimize(JavaFunction.java:59)
at org.h2.command.query.Select.prepareExpressions(Select.java:1170)
at org.h2.command.query.Query.prepare(Query.java:218)
at org.h2.command.Parser.prepareCommand(Parser.java:574)
at org.h2.engine.SessionLocal.prepareLocal(SessionLocal.java:631)
at org.h2.engine.SessionLocal.prepareCommand(SessionLocal.java:554)
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1116)
at org.h2.jdbc.JdbcPreparedStatement.(JdbcPreparedStatement.java:92)
at org.h2.jdbc.JdbcConnection.prepareStatement(JdbcConnection.java:288)
at com.db.cib.gbd.gps.pbs.pricing.StaticItemDetails.retriveItemDisplayDetails(StaticItemDetails.java:920)
This is my Java UDF:
public static String strip(String s, Expression loc, String trimConstant) {
if (loc.toLowerCase() == "b" || loc.toLowerCase() == "both") {
s = s.replaceAll("^[" + trimConstant + "]+|[ \t]+$", "");
} else if (loc.toLowerCase() == "l" || loc.toLowerCase() == "leading") {
s = s.replaceAll("^[" + trimConstant + "]+", "");
} else if (loc.toLowerCase() == "t" || loc.toLowerCase() == "trailing") {
s = s.replaceAll("[" + trimConstant + "]+$", "");
}
return s;
}
Is there a possibility to get the mapping of the column in the correct way, or can you suggest either a SQL function, which is usable as UDF alias (how is this usable?) or a way to solve this error?
To avoid this question: I cannot change the existing sql statement. I have to find an alias for this function.
It isn't possible to create a user-defined function with special arguments in H2 and most likely in all or almost all other database systems. User-defined functions accept only plain comma-separated arguments with literals or expressions in them.
(You also cannot declare a parameter as org.h2.expression.Expression.)
The proper solution here is to use the TRIM function from the SQL Standard:
https://h2database.com/html/functions.html#trim
https://www.ibm.com/docs/en/db2-for-zos/11?topic=functions-trim
TRIM(BOTH ' ' FROM T2.ITEM_TYPE_NAME_GER)
Please note that B, L, and T acronyms is a DB2-specific extension, in H2 you can use only standard BOTH, LEADING, and TRAILING.
If you cannot change your query you can only modify sources of H2 and compile its own version with support of the STRIP. But actually you can run into some other issue immediately. When you want to use multiple database systems at once you need to realize that they all are very different from each other. H2 provides compatibility modes for others, but even in these modes there is a very limited compatibility. It means you need to avoid usage of vendor-specific functions and other grammar elements and in some cases different SQL for different systems may be required.
You also can try to create constants B, BOTH, L, LEADING, T, and TRAILING with some values
CREATE CONSTANT B VALUE 1;
CREATE CONSTANT BOTH VALUE 1;
CREATE CONSTANT L VALUE 2;
…
and create a function with three arguments, second argument will be of type int (or other, if you'll decide to choose values of some other data type). But names of constants may conflict with column names, so this workaround is far from being perfect and may not work at all in some queries.
I am using jdbc PreparedStatement for data insertion.
Statement stmt = conn.prepareStatement(
"INESRT INTO" + tablename+ "("+columnString+") VALUES (?,?,?)");
tablename and columnString are something that is dynamically generated.
I've tried to parameterise tablename and columnString but they will just resolve to something like 'tablename' which will violate the syntax.
I've found somewhere online that suggest me to lookup the database to check for valid tablename/columnString, and cache it somewhere(a Hashset perhaps) for another query, but I'm looking for better performance/ quick hack that will solve the issue, perhaps a string validator/ regex that will do the trick.
Have anyone came across this issue and how do you solve it?
I am not a java-guy, so, only a theory.
You can either format dynamically added identifiers or white-list them.
Second option is way better. Because
most developers aren't familiar enough with identifiers to format them correctly. Say, to quote an identifier, which is offered in the first comment, won't make it protected at all.
there could be another attack vector, not entirely an injection, but similar: imagine there is a column in your table, an ordinary user isn't allowed to - say, called "admin". With dynamically built columnString using data coming from the client side, it's piece of cake to forge a privilege escalation.
Thus, to list all the possible (and allowed) variants in your code beforehand, and then to verify entered value against it, would be the best.
As of columnString - is consists of separate column names. Thus, to protect it, one have to verify each separate column name against a white list, and then assemble a final columnString from them.
Create a method that generates the sql string for you:
private static final String template = "insert into %s (%s) values (%s)";
private String buildStmt(String tblName, String ... colNames) {
StringJoiner colNamesJoiner = new StringJoiner(",");
StringJoiner paramsJoiner = new StringJoiner(",");
Arrays.stream(colNames).forEach(colName -> {
colNamesJoiner.add(colName);
paramsJoiner.add("?");
});
return String.format(template, tblName, colNamesJoiner.toString(), paramsJoiner.toString());
}
Then use it...
Statement stmt = conn.prepareStatement(buildStmt(tablename, [your column names]));
As an elaboration on #Anders' answer, don't use the input parameter as the name directly, but keep a properties file (or database table) that maps a set of allowed inputs to actual table names.
That way any invalid name will not lead to valid SQL (and can be caught before any SQL is generated) AND the actual names are never known outside the application, thus making it far harder to guess what would be valid SQL statements.
I think, the best approach is to get table and columns names from database or other non user input, and use parameters in prepared statement for the rest.
There are multiple solutions we can apply.
1) White List Input Validation
String tableName;
switch(PARAM):
case "Value1": tableName = "fooTable";
break;
case "Value2": tableName = "barTable";
break;
...
default : throw new InputValidationException("unexpected value provided for table name");
By doing this input validation on tableName, will allows only specified tables in the query, so it will prevents sql injection attack.
2) Bind your dynamic columnName(s) or tableName(s) with special characters as shown below
eg:
For Mysql : use back codes (`)
Select `columnName ` from `tableName `;
For MSSQL : Use double codes(" or [ ] )
select "columnName" from "tableName"; or
select [columnName] from [tableName];
Note: Before doing this you should sanitize your data with this special characters ( `, " , [ , ] )
I've got some SQL queries like this
select user_id from
table_user where lower(email_address)=? and password=?
The schema for the application was recently updated but I don't really want to update every SQL query in the application. Is there a way to specify the current Schema from the JBOSS connection end?
Old connection: jdbc:sqlserver://myserver:1433;DatabaseName=db
Tried: jdbc:sqlserver://myserver:1433;DatabaseName=db;currentSchema=abc
I tried using currentSchema but that didn't help, I get a missing object exception when I run the queries (since I assume these are looking under dbo). Is there any way around updating the queries since I know that all the queries will run on schema abc?
These are the available connection properties for Microsoft JDBC 4.0 driver. I don't see currentSchema in this list, and haven't seen any driver that allows you to specify a particular schema in the connection string.
Since you don't want to update SQL with the schema, you could create synonyms in default (dbo) schema for each object. For example:
USE tempdb;
GO
-- create test schema
CREATE SCHEMA test AUTHORIZATION dbo;
GO
-- create table in test schema
CREATE TABLE test.tablename (columnname int null);
-- select from tablename in default schema will fail
SELECT * FROM tablename;
GO
-- create synonym mapping test.tablename to dbo.tablename
CREATE SYNONYM [dbo].[tablename] FOR [server].[tempdb].[test].[tablename]
-- -- select from tablename synonym will succeed
SELECT * FROM tablename;
-- cleanup
DROP SYNONYM [dbo].[tablename];
DROP TABLE [test].[tablename];
DROP SCHEMA [test];
You can use the below code to generate CREATE SYNONYM statements for user objects. If you use it, you'll need to update variable values and review statements before executing. No warranty express or implied :)
-- generate create synonym statements for user objects
DECLARE #FromSchema SYSNAME = 'abc',
#ToSchema SYSNAME = 'dbo',
#ServerName SYSNAME = 'server',
#DatabaseName SYSNAME = 'database';
SELECT 'CREATE SYNONYM ' + QUOTENAME(#ToSchema) + '.' + QUOTENAME(name) +
' FOR ' + QUOTENAME(#ServerName) + '.' + QUOTENAME(#DatabaseName) +
'.' + QUOTENAME(#FromSchema) + '.' + QUOTENAME(name) + ';'
FROM sys.objects
WHERE is_ms_shipped = 0;
Problem: Report Templates are created by Admin Users who only decide what data to show where as the filter for the data is specified by the business user. In simpler SQL terms, query is specified by the Admin User, Business User specifies the WHERE clause.
Jasper allows user to specify parameters in SQL query like $P{city}. I have tried to retrieve the query dynamically using the method specfied in the link.
Possible Solution can be
Use WHERE clause parameters in JRXML and replace them while report creation - This will save me SQL parsing but I don't want to guide the admin user with this complexity. Parsing is not a huge problem.
Use my custom jdbc query executor and factory, only created to allow me extension point before jasper fire SQL query. I will be completely relying on vanilla Jasper JDBC data source but will only modify query before execution. JRAbstractQueryExecuter simplifies the query and replace the jasper related tokens before firing query - This will be very dirty and will force me to be implementation specific.
Do the same replacement as it is done in JRAbstractQueryExecuter in my application code base, parse the SQL query, modify it and set it again as specified in link
Can you please suggest a better way of doing this? I have a feeling that this can definitly be done in cleaner way.
Thanks for your help
You could create an input control to determine the desired WHERE clause and use a parameter to hold the contents of that WHERE clause. The default value expression would be something like:
$P{theParameter} == "value_1" ?
(" AND CONDITION_1 IN ('A', 'B', 'C') AND CONDITION_2 = 'Yes' "
) : " AND CONDITION_3 = 'Other' AND CONDITION_4 = 'No' "
Then in your WHERE clause you would reference it like:
WHERE
.... = .....
AND .... = ....
AND .... = ....
$P!{theParameter}
If your constraint columns are the same across your WHERE clauses, you could use $P! to bring in the parameter value literally, and reference it in your query:
WHERE
.... = .....
AND .... = ....
AND .... = ....
AND thisValue = $P!{theParameter}
I'm using hibernate 3.6 and i'm having problems using criteria.list. I need search words with quotes or accents like "d'eau" or "d´eau". My code is something like this:
Criteria criteria;
criteria.add(Restrictions.ilike("nameParam", "d''eau", MatchMode.ANYWHERE));
I put two single quotes because i'm working with a sql server database. There are 2 single quotes because single quote escapes with another single quote.
The statement has 0 results, but if I execute the sql statement printed in the log in the sql server client, I get 120 results aprox.
Testing with HQL and the same sql statement. I get this:
String hqlQuery = "select distinct(t) from Table t where b.idCon in (select t2.idCon from Table t2 where lower(t2.outTerTb) like '%d''eau%')";
List qwer = getEntityManager().createQuery(hqlQuery).getResultList();
System.out.println("qwer.size() -> " + qwer.size());
String hqlQuery2 = "select distinct(t) from Table t where b.idCon in (select t2.idCon from Table t2 where lower(t2.outTerTb) like :param)";
List qwer2 = getEntityManager().createQuery(hqlQuery2).setParameter("param", "%d''eau%").getResultList();
System.out.println("qwer2.size() -> " + qwer2.size());
This code print:
qwer.size() -> 120
qwer2.size() -> 0
And I don't understand why this happens.
Sorry if my english is bad
You don't need to escape single quotes in your parameters. That's the whole point of using parameters (in Hibernate and, behind the scenes, in the JDBC prepared statements): the JDBC driver escapes everything that needs to be escaped for you.
You take what comes from the UI layer as is and stuff it into parameters, everything isproperly escaped by the driver, and you don't risk any SQL injection attack.