Running IBM Db2 DDL statements in JDBC - java

I need to alter a Db2 column using JDBC. The column may change its name and/or its type. In Db2 these two actions are done in two steps, the first ALTER TABLE to change the name, and the second ALTER TABLE to change the type.
For example:
ALTER TABLE T1 RENAME COLUMN C1 TO C2;
ALTER TABLE T1 ALTER COLUMN C2 SET DATA TYPE decimal(4,0);
See below the code, the first statement is executed but the second always throws an exception.
String sql = "ALTER TABLE " + tableName + " RENAME COLUMN " +
originalName + " TO " + name;
PreparedStatement ps1 = conn.prepareStatement(sql);
ps1.executeUpdate();
sql = "ALTER TABLE " + tableName + " ALTER COLUMN " + name +
" SET DATA TYPE decimal(" + sc.getLength() + "," + sc.getDec() + ")";
PreparedStatement ps2 = conn.prepareStatement(sql);
ps2.executeUpdate();
The exception is:
The operation was not performed because the table is in an invalid
state for the operation. Table name: "DB.T1".
Reason code: "23".. SQLCODE=-20054, SQLSTATE=55019, DRIVER=4.27.25
What is the meaning of a table in an "invalid state"? Why is the table in this state? What's wrong with this code?

Always give your Db2-server platform (z/os, linux/unix/windows, i series) and Db2-server version when asking for Db2-help, because the answer can depend on these facts.
The exception SQL20054N reason 23, means that the table has reached a limit on the number of alterations and before continuing, the table need to be reorganized with a REORG command. The documentation for the error is here. The REORG command will put the table back into a normal state. Normally a DBA would consider running RUNSTATS command following the REORG to ensure that table statistics are refreshed following the alterations.
Db2-LUW allows a small number of table changes (often 3) before forcing a reorg for certain kinds of alterations. Previous alterations to this table might have been performed by others, in different transactions , without getting this exception. Schema-evolution tools should detect this state and recover from it.
This is a normal situation, and the recovery is to run the REORG command.
You can either ask your DBA to do reorg for you, or you can (if your authid has the correct permissions) from jdbc call a stored procedure admin_cmd() to perform the command for you, or just use the Db2 command line interface reorg table db.t1 inplace for example . The documentation for admin_cmd is here, and if you do not understand the REORG details, ask your DBA for help.

Related

Can't delete record from MySQL database

Trying to delete record from my database, but I get the error "Unknown column '' in 'where clause'".
private void deleteUser() {
String query = "DELETE FROM user WHERE Name =" + tfemail.getText() + "";
executeQuery(query);
showUsers();
}
You can't write queries this way. Imagine someone put in the tfemail field this text:
"Joe' OR FALSE"
and let's see what that would do to your SQL query:
DELETE FROM user WHERE Name = 'Joe' OR FALSE;
bye, database!
Some dbs let you execute stuff on the server the db engine runs on. Which means this trick can be used to completely hack the machine or format the disk entirely. bye, entire machine.
This also means your executeQuery method needs to be removed - that abstraction ('here is some SQL, please run it') is rarely useful (as it cannot contain any user input), and entices you to write security leaks.
The solution is prepared statements:
PreparedStatement ps = con.prepareStatement("DELETE FROM user WHERE Name = ?");
ps.setString(1, "Joe");
ps.executeUpdate();
This solves your problem, and does so safely - ps.setString(1, "Joe' OR FALSE"); is now no longer an issue (the DB engine or JDBC driver guarantees that it will take care of the problem; the effect would be to delete the entry in your user table that literally reads "Joe' OR FALSE").
Furthermore, storing passwords in a database is not an acceptable strategy; the solution is e.g. bcrypt: Use a hashing algorithm designed specifically to store passwords.
String query = "DELETE FROM user WHERE Name ='" + tfemail.getText() + "'";
^ ^
|___________add___________|

Error in SQL syntax when running SQL in Java but not in HeidiSQL

I'm trying to run an insert or update on a table - the string generated from below works fine when copy pasted into HeidiSQL but throws SQLSyntaxErrorExceptions when run from Java:
Statement statement = con.createStatement();
String escapedXML = EscapeString(billboard.getXml());
String sql = String.format(
"DELIMITER $ \r\nBEGIN NOT ATOMIC\r\n" +
"IF EXISTS(SELECT * FROM billboards where Name='%s') THEN UPDATE billboards SET XML='%s' where Name='%s';\r\n" +
"ELSE insert into billboards(Name, XML, CreatorName) values('%s', '%s', '%s');\r\n" +
"END IF;\r\n" +
"END $\r\n" +
"DELIMITER ;", billboard.getName(), escapedXML, billboard.getName(), billboard.getName(), escapedXML, billboard.getCreatorName());
// Insert or update billboard
statement.execute(sql);
I can't figure out why.
I would recommend using the insert ... ok duplicate key syntax here rather than a code block. This is more efficient, and implements the lockout a single statement, which should avoid the problem you meet when running the query from your php code.
insert into billboards(Name, XML, CreatorName)
values(?, ?, ?)
on duplicate key update set XML = values(XML)
For this to work, you need a unique (or primary key) constraint on column Name.
Also, consider using a parameterized query rather than concatenating variables in your query stringW Escaping is inefficient and does not really make your code safer.
You should have tried NamedParameterStatement with your query to facilitate setting of string parameters and avoid their duplication (using refactored query suggested in GMB's earlier answer):
String sql = "INSERT INTO billboards (Name, XML, CreatorName) VALUES (:name, :xml, :creator) "
+ "ON DUPLICATE KEY UPDATE SET XML = :xml";
NamedParameterStatement statement = new NamedParameterStatement(con, sql);
statement.setString("name", billboard.getName());
statement.setString("xml", EscapeString(billboard.getXml()));
statement.setString("creator", billboard.getCreatorName());
// Insert or update billboard
statement.execute(sql);
The reason that you are getting a syntax error is that DELIMITER is a MySQL client command and not an SQL statement. MySQL commands may not be used in with JDBC.
For more information:
Delimiters in MySQL

UcanaccessSQLException: unexpected token: ORDER

I'm making a little app in Java and MySQL with PHPMyAdmin and all runs fine, but my professor says that we have to work with a database in Access, so I just changed my class connection and imported my database. The INSERT, SELECT and other UPDATE statements run fine but this statement just doesn't run.
UPDATE table SET col1=?, col2=? WHERE col0=? ORDER BY col4 DESC LIMIT 1
I can't understand how in MySQL it runs fine but with UCanAccess it doesn't work.
I can't understand how in MySQL it runs fine but with UCanAccess it doesn't work.
That's because the various producers of database software have taken it upon themselves to implement the SQL language in slightly different ways, so a given SQL statement written for MySQL is not guaranteed to work under Access, or Microsoft SQL Server, or Oracle, or any other "dialect" of SQL.
UCanAccess tries very hard to follow the Access SQL syntax. Access SQL uses TOP n instead of LIMIT n, but Access SQL also does not allow TOP n or ORDER BY in the main part of an UPDATE query. So you need to use a subquery to identify the primary key value of the row you want to update.
For example, if your table has a primary key column named "id" then you can do
sql =
"UPDATE table1 SET col1=?, col2=? " +
"WHERE id IN ( " +
"SELECT TOP 1 id " +
"FROM table1 " +
"WHERE col0=? " +
"ORDER BY col4 DESC, id " +
")";

executing sql constraints in JDBC code

So after creating tables using jdbc, I have this code to make one to many relationship between UserInfoTable and ContactTable and UserInfoID as foreign key.
String addConstraint = "alter table ContactTable"+
"ADD CONSTRAINT FK_ContactTable_UserInfoTable"+
"FOREIGN KEY(UserInfoID)"+
"REFERENCES UserInfoTable (UserInfoID)"+
"ON UPDATE CASCADE"+
"ON DELETE CASCADE";
But when I execute this,
con.prepareStatement(addConstraint).executeUpdate();
I'm getting
java.sql.SQLException: Incorrect syntax near the keyword 'CONSTRAINT'.
I'm really confused. I handcoded this query several times in sql server and I think my syntax is correct because it always executed successfully, why not when executed by java code?
You are concatenating strings but the different segments have no spaces, this could be causing keywords to be bunched together with your data creating invalid keywords.
It is simple but this may be all you need:
String addConstraint = "alter table ContactTable "+
"ADD CONSTRAINT FK_ContactTable_UserInfoTable "+
"FOREIGN KEY(UserInfoID) "+
"REFERENCES UserInfoTable (UserInfoID) "+
"ON UPDATE CASCADE "+
"ON DELETE CASCADE";

How can I specify the current schema for sql server in a jboss data source url?

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;

Categories