PLS-00103: Encountered the symbol "end-of-file" in FlyWay - java

We have a database migration scripts, which are run trough FlyWay, which is part of Java app. The problem is that some customers have the older version of app, where some table and data are presented, while others will be installing this product as new solution. These scripts must run everytime (solution problem and historical problem).
In fact, we handle this in SQL with simple check, if table exist and then execute the rest of script. It works quite fine for other databases (Postgres, MySQL, MS SQL) but not for Oracle. And after few days of trying and googling I am starting to rip my hair off of my head. First problem was executing DDL statements (already learned I can't do that) and now this.
What are we traing to achieve:
Run script if main table exists for current user (that's how we
determine, if all other table exists too)
For every table we need to create a backup table and copy all rows in it (thats why we do TRUNCATE and INSERT INTO and if fails,
we do CREATE TABLE AS) - this is what presented code should do, block of code inside IF table_count > 0 THEN and END IF; is repeated per every table presented (11x)
We then run the cleanup script, which checks for bad data and clean them (we must have original values stored in case something
goes bad).
Cleaned datas are copied into new tables (prepared in previous script).
There is 10 scripts in total, but this one is constantly failing.
Here is the code:
DECLARE
table_count NUMBER;
curr_user VARCHAR2(100);
BEGIN
SELECT
sys_context('USERENV','SESSION_USER') INTO curr_user
FROM dual;
SELECT count(*)
INTO table_count
FROM all_objects
WHERE object_type IN ('TABLE', 'VIEW')
AND object_name = 'main_table'
AND owner = curr_user;
IF table_count > 0 THEN
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE some_table_backup';
EXECUTE IMMEDIATE 'INSERT INTO some_table_backup
SELECT *
FROM some_table';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942
THEN
RAISE;
ELSE
EXECUTE IMMEDIATE 'CREATE TABLE some_table_backup AS SELECT * FROM some_table';
END IF;
END;
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE some_table_backup2';
EXECUTE IMMEDIATE 'INSERT INTO some_table_backup2
SELECT *
FROM some_table2';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942
THEN
RAISE;
ELSE
EXECUTE IMMEDIATE 'CREATE TABLE some_table_backup2 AS SELECT * FROM some_table2';
END IF;
END;
.
.
.
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line(SQLERRM);
END;
The error is following:
ORA-06550: line 30, column 8:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:
Line 30 is END; after END IF; in first partition of script.

It looks like that your version of FlyWay does not like nested PL/SQL Blocks. I would try to add anonymous block labels:
DECLARE
table_count NUMBER;
curr_user VARCHAR2(100);
BEGIN
SELECT
sys_context('USERENV','SESSION_USER') INTO curr_user
FROM dual;
SELECT count(*)
INTO table_count
FROM all_objects
WHERE object_type IN ('TABLE', 'VIEW')
AND object_name = UPPER('main_table') --here I added UPPER
AND owner = curr_user;
IF table_count > 0 THEN
<<name1>>
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE some_table_backup';
EXECUTE IMMEDIATE 'INSERT INTO some_table_backup
SELECT *
FROM some_table';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942
THEN
RAISE;
ELSE
EXECUTE IMMEDIATE 'CREATE TABLE some_table_backup AS SELECT * FROM some_table';
END IF;
END name1;
<<name2>>
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE some_table_backup2';
EXECUTE IMMEDIATE 'INSERT INTO some_table_backup2
SELECT *
FROM some_table2';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942
THEN
RAISE;
ELSE
EXECUTE IMMEDIATE 'CREATE TABLE some_table_backup2 AS SELECT * FROM some_table2';
END IF;
END name2;
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line(SQLERRM);
END;
/
Alternatively you could avoid nested PL/SQL block by changing approach.
Instead of trying to do something first and handle exception, you could easily check if table exist or not and then do the action. That way there is no need for nested PL/SQL blocks at all.
Second alternative is to use dynamic SQL:
DECLARE
table_count NUMBER;
curr_user VARCHAR2(100);
BEGIN
SELECT
sys_context('USERENV','SESSION_USER') INTO curr_user
FROM dual;
SELECT count(*)
INTO table_count
FROM all_objects
WHERE object_type IN ('TABLE', 'VIEW')
AND object_name = UPPER('main_table')
AND owner = curr_user;
IF table_count > 0 THEN
EXECUTE IMMEDIATE q'{
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE some_table_backup';
EXECUTE IMMEDIATE 'INSERT INTO some_table_backup
SELECT *
FROM some_table';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942
THEN
RAISE;
ELSE
EXECUTE IMMEDIATE 'CREATE TABLE some_table_backup AS SELECT * FROM some_table';
END IF;
END;}';
EXECUTE IMMEDIATE q'{
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE some_table_backup2';
EXECUTE IMMEDIATE 'INSERT INTO some_table_backup2
SELECT *
FROM some_table2';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942
THEN
RAISE;
ELSE
EXECUTE IMMEDIATE 'CREATE TABLE some_table_backup2 AS SELECT * FROM some_table2';
END IF;
END;}';
END IF;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line(SQLERRM);
END;
Please note the usage of q'{}' text literal that allow to handle ' without duplicating them.
With FlyWay 4.2.0(called from command-line) both samples work as it should.

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.

Incorrect Syntaxt Near Go Sql SSMS 17

I have a .sql file which includes lots of create function and other statements. I put go keyword between them but I got Incorrect Syntaxt Near Go error when I run this by stmt.executeupdate() in java. But when I run it in SSMS 17 I do not get any error. For example here:
GO
IF EXISTS (SELECT *
FROM sys.objects
WHERE object_id = OBJECT_ID(N'[dbo].[calculateweekday]')
AND type IN ( N'FN', N'IF', N'TF', N'FS', N'FT' )) begin
DROP FUNCTION [dbo].[calculateweekday]
end
GO
create function [dbo].[calculateweekday]
(#inputDate datetime)
returns int as begin
Declare #result int;
Declare #tmp int
select #tmp = (select DATEPART(weekday, #inputDate))
if (#tmp != 1) begin
select #result = (select #tmp - 1)
end else begin
select #result = (select 7)
end
return #result
end
GO
I tried to put this in notepad++ too see if there is something wrong but it look likes same.
I also tried to change the encoding UTF-8 and ANSI but could not solve it. How can I solve this?

execute anonymous pl/sql block file with values from java

I have an anonymous pl/sql block in an sql file and I want to execute and assign values to it in Java. My sql block looks like this
DECLARE
someInput1 NUMBER(1);
someInput2 NUMBER(2);
someString1 VARCHAR(100);
BEGIN
someInput1 := ‘&1’;
someInput2 := ‘&2’;
--get name in table A
BEGIN
SELECT a.value INTO someString1
FROM TABLE_A a
WHERE a.id = someInput1;
END;
UPDATE TABLE_B b
SET b.someStringRow = someString1
WHERE b.someIntRow = someInput2;
COMMIT;
END;
/
exit;
What I am planning to do is load the sql file in a Java String, change ‘&1’ to ?1 and execute it as a CallableStatement. However, I am getting
PLS-00103: Encountered the symbol “” when expecting one of the following
begin function package pragma procedure subtype us <an identifier> <a double quoted delimited identifier> form current cursor
Error occured at lines in DECLARE section
Is my approach an acceptable solution, if yes, what may be wrong in my approach?
Are there other better solution for my problem? Thanks
you have to remove quote marks, and replace &1 with :1, and you should be fine, also, you will need remove '/' and exit; and they relate to sqlplus and not to pl/sql directly

Detect, delete empty columns and update database in sql, oracle

I have 100 of columns and some of the doesn't have any values inside(they are empty) how can I search for empty columns and delete from table and update database? I tried this query but it doesnt work. It shows 0 rows selected. After selecting how can I update the database?
select table_name, column_name
from all_tab_columns
where table_name='some_table'
and column_name is NULL;
Thanks,
You are querying a data dictionary view. It shows meta-data, in formation about the database. This view, ALL_TAB_COLUMNS, shows information for every column of every table (you have privileges on). Necessarily COLUMN_NAME cannot be null, hence your query returns no rows.
Now what you want to do is query every table and find which columns have no data in them. This requires dynamic SQL. You will need to query ALL_TAB_COLUMNS, so you weren't completely off-base.
Because of dynamic SQL this is a programmatic solution, so the results are displayed with DBMS_OUTPUT.
set serveroutput on size unlimited
Here is an anonymous block: it might take some time to run. The join to USER_TABLES is necessary because columns from views are included in TAB_COLUMNS and we don't want those in the result set.
declare
dsp varchar2(32767);
stmt varchar2(32767);
begin
<< tab_loop >>
for trec in ( select t.table_name
from user_tables t )
loop
stmt := 'select ';
dbms_output.put_line('table name = '|| trec.table_name);
<< col_loop >>
for crec in ( select c.column_name
, row_number() over (order by c.column_id) as rn
from user_tab_columns c
where c.table_name = trec.table_name
and c.nullable = 'Y'
order by c.column_id )
loop
if rn > 1 then stmt := concat(stmt, '||'); end if;
stmt := stmt||''''||crec.column_name||'=''||'
||'to_char(count('||crec.column_name||')) ';
end loop col_loop;
stmt := stmt || ' from '||trec.table_name;
execute immediate stmt into dsp;
dbms_output.put_line(dsp);
end loop tab_loop;
end;
sample output:
table name = MY_PROFILER_RUN_EVENTS
TOT_EXECS=0TOT_TIME=0MIN_TIME=0MAX_TIME=0
table name = LOG_TABLE
PKG_NAME=0MODULE_NAME=0CLIENT_ID=0
PL/SQL procedure successfully completed.
SQL>
Any column where the COUNT=0 has no values in it.
Now whether you actually want to drop such columns is a different matter. You might break programs which depend on them. So you need an impact analysis first. This is why I have not produced a program which automatically drops the empty columns. I think that would be dangerous practice.
It is crucial that changes to our database structure are considered and audited. So if I were ever to undertake an exercise like this I would alter the output from the program above so it produced a script of drop column statements which I could review, edit and keep under source control.

Categories

Resources