I have the following select-query creation:
final DSLContext create = DSL.using(..., SQLDialect.POSTGRES);
create
.select(DSL.field("identifier"), DSL.field("name"),
create.selectCount()
.from(DSL.table("person"))
.where(DSL.field("identifier").eq(DSL.field("personOuter.identifier")))
.asField("count"))
.from(DSL.table("person").as("personOuter"))
jOOQ generates the following query:
select
identifier,
name,
(select count(*)
from person
where identifier = personOuter.identifier) as "count"
from person as "personOuter"
The query should be:
select
identifier,
name,
(select count(*)
from person
where identifier = personOuter.identifier) as "count"
from person as personOuter
The latter query works perfectly in PostgreSQL. The table alias should not be surrounded by quotes.
Is this a bug?
(Note that the query is pretty dumb. I am playing around with jOOQ to evaluate.)
The following "hack" works:
create
.select(DSL.field("identifier"), DSL.field("name"),
create.selectCount()
.from(DSL.table("person"))
.where(DSL.field("identifier").eq(DSL.field("personOuter.identifier")))
.asField("count"))
.from("person as personOuter")
A note on using the code generator
I'm assuming you have a good reason to avoid using the code generator (e.g. you work on a dynamic schema), because working with generated code prevents having to worry about such details. Plus, you get access to many advanced features, like implicit joins, embeddable types, etc.
What's a string in the jOOQ API?
By default, jOOQ will wrap all your identifiers in quotes in order to be able to handle case-sensitivity correctly.
The confusing part is why this isn't done for DSL.field(String), but only for Field.as(String). The reason for this is that jOOQ re-uses the String type for both:
Plain SQL as in DSL.field(String), where the input String doesn't really represent an identifier, but an arbitrary SQL expression
Identifiers as in DSL.name(String), where the input String represents a name / identifier. There is also DSL.fieldByName(String) to create Field types composed of (schema) / table / column identifiers.
In order to remove the quotes from all generated identifiers, you can also change the Settings.renderNameStyle to RenderNameStyle.AS_IS.
More information about Settings can be found here. And also in this blog post about "What’s a “String” in the jOOQ API?"
Related
I'm using database metadata to find some tables in a given schema in database like that:
DatabaseMetaData dbmd = connections.getMetaData();
ResultSet rs = dbmd.getTables(null,"schema_name","table_name_pattern","type");
It works, but my problem is that I only want to find tables that begin with t and three others tables for which I have the exact names:
books_table, froots, and colors.
How can I make a pattern that gives me only these three tables and the tables that begin with t?
I had a similar problem time ago, and the answer is no, you can't do it this way.
The JDBC metadata classes have only very limited functionality.
However, you can query the engine metadata directly. For example you can do:
select *
from pg_catalog.pg_tables
where schemaname = 'public'
and tablename like 't%' or tablename in ('books_table', 'froots', 'colors')
The parameter is interpreted as as wildcard value for a LIKE condition. So to find all tables starting with t use t%.
Quote from the JavaDocs
Some DatabaseMetaData methods take arguments that are String patterns. These arguments all have names such as fooPattern. Within a pattern String, "%" means match any substring of 0 or more characters, and "_" means match any one character.
Note that this is case-sensitive. If you created your tables without quotes (which is what you should be doing) they are stored in lowercase, so t% will work fine.
The last parameter should be a string array with all possible "object types". As you want tables, use {"TABLE"}. Alternatively if you don't care about the "type", you can also pass null
DatabaseMetaData dbmd = connections.getMetaData();
ResultSet rs = dbmd.getTables(null,"schema_name","t%",new String[]{"TABLE"});
You can't however specify multiple OR conditions. If you want to find if other tables exist where you can't use a wildcard, you will have to call getTables() once for each table name.
Another option is to simply get all tables from that schema, then discard those that you don't want while processing the ResultSet. That is most probably faster than using multiple calls to getTables()
I write a scala program which interoperates with some database engines (for example MySQL, PostgreSQL).
I use JDBC api to handle SQL queries. I use queries to create table, and I want to create a table with the fields given by users, then these fields are names which can contains spaces or words with accent.
For example, create a table dummy with 2 fields, 'column 1' and 'column 2' as varchar fields.
Writing this query for MySQL database while preserving the spaces contained in the fields, we need to use backticks in the query like :
CREATE TABLE dummy (`column 1` varchar(20), `column 2` varchar(20));
In the same way, the right way to write this query for PostgreSQL while preserving the spaces is :
CREATE TABLE dummy ("column 1" varchar(20), "column 2" varchar(20));
Maybe for another database engine, there is a different way to write this query.
Is there any standard way to write this query with the constrainsts above and using JDBC so that it works with any database engines ?
Thank in advance for your answers.
This doesn't directly address your question as asked, but I think that you would be better off not naming your columns this way. I would 'normalize' the column names (by, e.g. replacing spaces with underscores).
The column names should probably not be exposed directly to the users anyway.
If you need 'human readable' names for columns, I would store them in another table. Or, if it is as simple as preserving spaces, just reverse the process, replacing underscores with spaces.
As already mentioned you should not use spaces, special characters or reserved words as column or table names. To play it safe you can generally put quotes around table and column names to avoid case-sensitivy issues accross databases.
CREATE TABLE "foo" ("id" VARCHAR(32), "bar" VARCHAR(64))
According to SQL-99 standard double quotes (") are used to delimit identifiers. Case sensitivy actually relates to the type of database used and it's settings. There are dbs that will upper- or lowercase your table and column names if they are not quoted but sometimes only in CREATE TABLE or ALTER TABLE commands. Which can lead to runtime errors.
For example CREATE TABLE foo might actually create a table named FOO. When doing a SELECT * FROM foo you might get an error because table foo does not exist but table FOO does exist. Having worked accross lots of DBMS I tend to use quotes for table and column names.
The important part is that you have to stick to writing lowercase or uppercase when using quotes because "foo" is not equal to "FOO" but foo might be equal to FOO depending on the used DBMS. Either do lowercase or uppercase but stick to it if you're using quotes.
You should also avoid database specific column types (stick to ANSI SQL whenever possible).
But doing database migrations by hand is very tedious and error-prone. I would suggest to use migration tools (flyway db) or let the migrations get created by libraries like slick.
As you mentioned scala, please have a look at Slick (http://slick.lightbend.com/) which is a great functional database layer for scala. There are others too but that one I use heavily and can recommend it.
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");
My question is similar to
Is there any good dynamic SQL builder library in Java?
However one important point taken from above thread:
Querydsl and jOOQ seem to be the most popular and mature choices however there's one thing to be aware of: Both rely on the concept of code generation, where meta classes are generated for database tables and fields. This facilitates a nice, clean DSL but it faces a problem when trying to create queries for databases that are only known at runtime.
Is there any way to create the queries at runtime besides just using plain JDBC + String concatenation?
What I'm looking for is a web application that can be used to build forms to query existing databases. Now if something like that already exists links to such a product would be welcome too.
While source code generation for database meta data certainly adds much value to using jOOQ, it is not a prerequisite. Many jOOQ users use jOOQ for the same use-case that you envision. This is also reflected in the jOOQ tutorials, which list using jOOQ without code generation as a perfectly valid use-case. For example:
String sql = create.select(
fieldByName("BOOK","TITLE"),
fieldByName("AUTHOR","FIRST_NAME"),
fieldByName("AUTHOR","LAST_NAME"))
.from(tableByName("BOOK"))
.join(tableByName("AUTHOR"))
.on(fieldByName("BOOK", "AUTHOR_ID").eq(
fieldByName("AUTHOR", "ID")))
.where(fieldByName("BOOK", "PUBLISHED_IN").eq(1948))
.getSQL();
In a similar fashion, bind values can be extracted from any Query using Query.getBindValues().
This approach will still beat plain JDBC + String concatenation for dynamic SQL statements, as you do not need to worry about:
Syntax correctness
Cross-database compatibility
SQL Injection
Bind variable indexing
(Disclaimer: I work for the vendor of jOOQ)
SQLBuilder http://openhms.sourceforge.net/sqlbuilder/ is very useful for me.
Some simple examples:
String query1 = new InsertQuery("table1")
.addCustomColumn("s01", "12")
.addCustomColumn("stolbez", 19)
.addCustomColumn("FIRSTNAME", "Alexander")
.addCustomColumn("LASTNAME", "Ivanov")
.toString();
String query2 = new UpdateQuery("table2")
.addCustomSetClause("id", 1)
.addCustomSetClause("FIRSTNAME", "Alexander")
.addCustomSetClause("LASTNAME", "Ivanov")
.toString();
Results:
INSERT INTO table1 (s01,stolbez,FIRSTNAME,LASTNAME) VALUES ('12',19,'Alexander','Ivanov')
UPDATE table2 SET id = 1,FIRSTNAME = 'Alexander',LASTNAME = 'Ivanov'
I have a custom solution for dynamically generating such SQL queries with just 2-3 classes for similar requirement. It is a simple approch.
This can be referred at Creating Dynamic SQL queries in Java
For simpler use cases like a dynamic filter condition based on the inputs selected from UI, one can use the below simpler approach by directly modifying the query in below style:
select t1.id, t1.col1, t1.col2,
from table1 t1
where (:col1Value is null or t1.col1 = :col1Value)
and (:col2Value is null or t1.col2 = :col2Value);
Here values for col1 or col2 can be null but the query will work fine.
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.