Postgres flyway create concurrently index error - java

I want do something like:
CREATE OR REPLACE FUNCTION __column_exists(TEXT, TEXT, TEXT) RETURNS bool as $$
SELECT exists(SELECT 1 FROM information_schema.columns WHERE (table_schema, table_name, column_name) = ($1, $2, $3));
$$ language sql STRICT;
DO $$ BEGIN IF __column_exists('public', 'table_name', 'column_name') THEN
CREATE INDEX CONCURRENTLY IF NOT EXISTS column_idx ON table_name USING btree (column_name);
END IF; END; $$;
But there is
ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block
It works fine if I write only
CREATE INDEX CONCURRENTLY IF NOT EXISTS column_idx ON table_name USING btree (column_name);
How can I preserve the condition for the existence of a column?
Flyway 4.2.0;

Why are you using such an old version of flyway ?
You should first upgrade your version of flyway, and then:
Create a configuration file for your script like V01_001__my_script.sql.conf
where V01_001__my_script.sql is the name of your sql file.
and put this line inside:
executeInTransaction=false
That will disable the execution in a transaction for this script.

Related

How to fix "relation <table_name> does not exist" ERROR even when using 'IF EXISTS-THEN' pgsql block?

We are using Postgres 13.0 version with Spring-Boot .sql file as an initial step.
I need to run an UPDATE script but only if the table itself already exists.
After some effort to understand what is the correct syntax I came with the following script:
ALTER TABLE IF EXISTS ONLY scm_repos ADD COLUMN IF NOT EXISTS token_id BIGINT;
DO '
BEGIN
IF EXISTS
(SELECT 1 FROM scm_repos WHERE id = 1)
THEN
UPDATE scm_repos repos SET token_id=(SELECT token_id FROM scm_orgs orgs WHERE repos.org_id=orgs.id);
END IF ;
END;
' ;
My intention is simple - to run the UPDATE script only if the scm_repos table does exists, but whatever I tried, I'm still getting the following error:
Failed to execute SQL script statement #5 of URL [jar:file:/app/cx-integrations-datastore.jar!/BOOT-INF/classes!/schema.sql]: DO '
BEGIN
IF EXISTS
(SELECT 1 FROM scm_repos WHERE id = 1)
THEN
UPDATE scm_repos repos SET token_id=(SELECT token_id FROM scm_orgs orgs WHERE repos.org_id=orgs.id);
END IF ;
END;
' ; nested exception is org.********ql.util.PSQLException: ERROR: relation "scm_repos" does not exist
Where: PL/pgSQL function inline_code_block line 3 at IF
What am I missing here?
13.0 has known unfixed bugs. 13.4 is the latest release of 13. There is almost never a good reason to run an old minor release version. Not that that seems to be relevant here.
But what you are missing here is that at the top level, EXISTS checks to see if a SELECT returns any rows. It does not check to see if tables mentioned in the FROM list of the SELECT exist or not, it assumes they do.
You could change your query so that it queries the catalog to see if the table exists, something like:
IF EXISTS
(SELECT 1 FROM pg_class where relname=$J$scm_repos$J$)
...

iBatis' ScriptRunner Is Failing To Execute a Perfectly Valid SQL Script

I'm using iBatis' ScriptRunner to execute scripts on an Oracle database. The first script is executed fine, but the second one which has triggers in it returns:
Cause: java.sql.SQLSyntaxErrorException: ORA-00900: invalid SQL statement
The part of the script which returns this error is executed without any errors on SQL Developer:
.
.
.
create table MG_MSGALR
(
ID VARCHAR2(30) not null,
V_GRAV VARCHAR2(3),
constraint PK_MG_MSGALR primary key (ID) using index tablespace B_INDEX
);
CREATE OR REPLACE TRIGGER UC_JAR_LST_TRIGGER
BEFORE INSERT
ON UC_JAR_LST
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT UC_JAR_LST_SEQ.nextval INTO :NEW.ID FROM dual;
END;
/
CREATE OR REPLACE TRIGGER UC_UPD_LST_TRIGGER
BEFORE INSERT
ON UC_UPD_LST
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT UC_UPD_LST_SEQ.nextval INTO :NEW.ID FROM dual;
END;
/
Here's how I execute the script from my side:
Boolean procedure = StringUtils.endsWith(FilenameUtils.getBaseName(file.getName()), "procedure") || StringUtils.endsWith(FilenameUtils.getBaseName(file.getName()), "trigger");
runner.setSendFullScript(procedure);
runner.runScript(new FileReader(file));
I noticed that the Boolean procedure's value is always false even when the script has triggers in it, and so I tried to force ScriptRunner to send it as a full script just to see if it goes through or not and i got the following error instead:
CREATE OR REPLACE TRIGGER UC_UPD_LST_TRIGGER
BEFORE INSERT
ON UC_UPD_LST
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT UC_UPD_LST_SEQ.nextval INTO :NEW.ID FROM dual;
END;
/
. Cause: java.sql.SQLSyntaxErrorException: ORA-00933: SQL command not properly ended
Could somebody please tell me what am I doing wrong here? Should add some sort of delimiter in the file right before when the trigger creation is supposed to start (which is now at the very end of the file).
In case someone else runs into the same problem. When you have a hybrid script (which means it has both normal queries and procedures or triggers), and if you're trying to execute it using myBatis from Java, all you need to do is leave all of your procedures at the end of your script and put delimiters before and after them to let SQL know it should be executed as a block and not line by line. So here's how I added my delimiters:
-- Change the delimiter to '$'
-- #DELIMITER $
CREATE OR REPLACE TRIGGER UC_JAR_LST_TRIGGER
BEFORE INSERT
ON UC_JAR_LST
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT UC_JAR_LST_SEQ.nextval INTO :NEW.ID FROM dual;
-- Change the delimiter back to ';'
-- #DELIMITER ;
END;
-- Change the delimiter to '$'
-- #DELIMITER $
CREATE OR REPLACE TRIGGER UC_UPD_LST_TRIGGER
BEFORE INSERT
ON UC_UPD_LST
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
SELECT UC_UPD_LST_SEQ.nextval INTO :NEW.ID FROM dual;
-- Change the delimiter back to ';'
-- #DELIMITER ;
END;
And the execution ended without errors.

Create MySQL stored procedure using JPA Hibernate

I'm trying to create a stored procedure in a MySQL database using the contents of a text file:
USE myDatabase;
DROP PROCEDURE IF EXISTS myStoredProcedure;
DELIMITER $$
CREATE PROCEDURE myStoredProcedure
(
_description VARCHAR(50),
_value INT
)
BEGIN
INSERT INTO myTable
(
description,
value
) VALUES (
_description,
_value
);
SELECT
id,
description,
value
FROM myTable
WHERE id = LAST_INSERT_ID();
END;
$$
DELIMITER ;
I execute the SQL using a native query:
Query query = entityManager.createNativeQuery(queryText);
...
query.executeUpdate();
But it gets an error on the DROP PROCEDURE
I commented out the DROP PROCEDURE and then it gets an error on the DELIMITER
Basically, it gets an error on any line after the first semicolon.
It seems as if JPA hibernate is parsing my query and telling me there's a problem with it rather than passing the unadulterated text onto MySQL.
The sql runs in MySQL without error.
I can't find anything in Google about creating a stored procedure with JPA, only calling one.
Does anyone have any insight on what I might be doing wrong? Or if this is even possible.
This can be possible if you mention the following property in the url
spring.datasource.url=jdbc:mysql://localhost:3306/test?allowMultiQueries=true
The allowMultiQueries will instruct the driver to sent delimited queries to the database.
Please note that if you are using native queries be-aware of sql injection attack.
You dont need to put the delimiter(DELIMITER) explicitly.The sql statement
The following query works
SET myDatabase;
DROP PROCEDURE IF EXISTS myStoredProcedure;
CREATE PROCEDURE myStoredProcedure ( _description VARCHAR(50), _value INT )
BEGIN
INSERT INTO
myTable ( description, value )
VALUES ( _description, _value );
SELECT id, description, value
FROM myTable
WHERE id = LAST_INSERT_ID();
END;

How to import SQL function in Hibernate

I use Hibernate and Postgres.
I'm working on completely new project so it's convenient for me to delegate database creation to Hibernate and just verifying it's structure if needed.
During my development I configured hibernate to always delete and create tables when the app starts. I use import.sql to load initial data. I'd like to create a function in postgres.:
CREATE OR REPLACE FUNCTION someFunction() RETURNS boolean AS $$
BEGIN
RETURN (SELECT count(*) > 0 FROM user);
END; $$
LANGUAGE PLPGSQL;
I'd like this to be imported by Hibernate on startup.
However hibernate is not able to handle it due to this: $$
Caused by: org.postgresql.util.PSQLException: Unterminated dollar quote started at position 61 in SQL CREATE OR REPLACE FUNCTION someFunction() RETURNS boolean AS $$
BEGIN
RETURN (SELECT count(*) > 0 FROM user). Expected terminating $$
How can I make it work?

Issue with executing procedure in spring boot schema.sql file

I am using schema.sql file to CREATE/DROP tables in my Spring Boot application and it works fine.
But when I have added procedure for altering table:
DELIMITER $$
CREATE PROCEDURE Alter_Table()
BEGIN
IF NOT EXISTS( SELECT NULL
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'test_table'
AND table_schema = 'test'
AND column_name = 'cc_test_id') THEN
alter table test_table add cc_test_id VARCHAR(128) NOT NULL;
END IF;
END $$
call Alter_Table;
I received:
com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException exception.
However, execution of this procedure in MySQL workbench finishes with successful results.
So, should anyone know what is the reason for this issue, let me know?
Here is the solution I found that works well enough, though it is not ideal as you have to change your SQL script.
In your application.properties file change the DataSource separator property:
spring.datasource.separator=^;
Then update your schema.sql file to look as follows:
CREATE PROCEDURE Alter_Table()
BEGIN
IF NOT EXISTS( SELECT NULL
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'test_table'
AND table_schema = 'test'
AND column_name = 'cc_test_id') THEN
alter table test_table add cc_test_id VARCHAR(128) NOT NULL;
END IF;
END ^;
call Alter_Table ^;
The DELIMITER command only works with the MySQL CLI client and Workbench and will not work for Spring Boot database initialization. Once you have removed the DELIMITER commands, Spring Boot will still throw an exception as it will not understand the ; characters in the stored procedures are not separate statements, so you have to change the datasource separator property as a workaround.
Here's the fix:
Set your spring.datasource.separator property to ^^^ END OF SCRIPT ^^^, and Spring Boot will execute your entire schema.sql script as a single statement.
Here's why:
Spring Boot splits your schema.sql script into statements, then sends each statement to the database individually for execution. By default, the script is split on semicolons, as that is the default value of the spring.datasource.separator property (per the documentation). This causes your schema.sql to be split, and the first statement executed is the following:
DELIMITER $$
CREATE PROCEDURE Alter_Table()
BEGIN
IF NOT EXISTS( SELECT NULL
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_name = 'test_table'
AND table_schema = 'test'
AND column_name = 'cc_test_id') THEN
alter table test_table add cc_test_id VARCHAR(128) NOT NULL
This is invalid SQL because the dollar-quote is never terminated in the statement.
The javadoc for org.springframework.jdbc.datasource.init.ScriptUtils.EOF_STATEMENT_SEPARATOR provides a good explanation of how my suggested solution works:
End of file (EOF) SQL statement separator: "^^^ END OF SCRIPT ^^^".
This value may be supplied as the separator to executeSqlScript(Connection, EncodedResource, boolean, boolean, String, String, String, String) to denote that an SQL script contains a single statement (potentially spanning multiple lines) with no explicit statement separator. Note that such a script should not actually contain this value; it is merely a virtual statement separator.
Customizing database populator with the appropriate separator EOF_STATEMENT_SEPARATOR resolves this issue:
#Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {
var resource = new ResourceDatabasePopulator(new ClassPathResource("schema.sql"));
resource.setSeparator(ScriptUtils.EOF_STATEMENT_SEPARATOR);
var populator = new CompositeDatabasePopulator();
populator.addPopulators(resource);
}
None of the above answers were working for me in 2022, but they provide a point of focus to come up with my own working solution(s). I posted my answer under the similar question, but for completeness to save you some extra clicks , I'm providing it here too.
I had been facing this issue too and just now I found a solution. Actually 3! So you can choose any of them which suits your needs. But the key to it is the correctly formatted String which represents the DB scripts to create a SP.
I haven't figure out how to combine dropping and creation of a SP in one SQL script/String, but I'm happy with those solutions anyway.
FYI:
No special annotation or setting on the class nor method level is needed
I'm using Spring FW, which simplifies things, so don't need to do low level implementation of everything
After searching and trying various definitely working 'tips', the following resources helped me to arrive to those for sure working solutions:
Execute SQL file from Spring JDBC Template
similar question Spring and MySQL stored procedure on stackoverflow, where I have actually posted my answer there
but most useful was official Oracle tutorial on JavaSE & JDBC
and other Java/JDBC tutorial where you can see the most important bit of formatting the sql Java String
Solution 1: using JdbcTemplate and Java String
It is very important to have trailing spaces in your nicely formatted String sql script
String sqlSP = "CREATE PROCEDURE test_stored_proc (IN pInstanceId varchar(255)) " +
"BEGIN " +
"SELECT * FROM vw_subscriptions WHERE instanceId = pInstanceId; " +
"END";
jdbcTemplate.execute(sqlSP);
Solution 2: using ResourceDatabasePopulator & ClassPathResource to load SQL script in a file
ClassPathResource resource = new ClassPathResource("/data/create-sp_TEST_STORED_PROC.sql");
jdbcTemplate.execute("DROP procedure IF EXISTS `test_stored_proc`;");
ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator(resource);
databasePopulator.setSeparator(ScriptUtils.EOF_STATEMENT_SEPARATOR);
databasePopulator.execute(testSetupDataSource);
Solution 3: using ScriptUtils & ClassPathResource to load SQL script in a file
ClassPathResource resource = new ClassPathResource("/data/create-sp_TEST_STORED_PROC.sql");
jdbcTemplate.execute("DROP procedure IF EXISTS `test_stored_proc`;");
ScriptUtils.executeSqlScript(Objects.requireNonNull(testSetupDataSource).getConnection(), new EncodedResource(resource),
false, false,
ScriptUtils.DEFAULT_COMMENT_PREFIX, ScriptUtils.EOF_STATEMENT_SEPARATOR,
ScriptUtils.DEFAULT_BLOCK_COMMENT_START_DELIMITER, ScriptUtils.DEFAULT_BLOCK_COMMENT_END_DELIMITER);
Content of the create-sp_TEST_STORED_PROC.sql script
For solution #1 and #2 the same rule as for the 1st solution applies:
The formatting and presence of whitespace characters is important especially an extra trailing space at the end of each line.
So the below code is archived by my vim setup of representing whitespace characters
CREATE·PROCEDURE·test_stored_proc·(IN·pInstanceId·varchar(255))~¬
BEGIN~¬
–→SELECT·*·FROM·vw_subscriptions·WHERE·instanceId·=·pInstanceId;~¬
END;~¬
I believe it is internally represented as one line piece of string:
CREATE PROCEDURE test_stored_proc (IN pInstanceId varchar(255)) BEGIN SELECT * FROM vw_subscriptions WHERE instanceId = pInstanceId; END
Almost full source-code can be found at my GitHub
If you try to load scripts for SpringBootTest, #Sql and #SqlConfig will work.
#Sql(
"/db/integration/stored-procedures.sql",
config = SqlConfig(separator = "$$")
)
In sql scripts, you should remove the DELIMITER $$ statement. Like this:
CREATE PROCEDURE <sp_name>()
<your owesome definition>
END $$
If the script file contains only one CREATE PROCEDURE statement, separator=EOF_STATEMENT_SEPARATOR will be fine.

Categories

Resources