ERROR at line 1: ORA-00911: invalid character ORA-06512: at line 17 - sql

I am not a frequent user of database & once in a while i need to create/run/execute a few PL/SQL blocks. I have a similar situation right now, where I have the below block which while executing as SYS as SYSDBA in oracle database user throws the error :-
DECLARE
*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at line 17
The PL/SQL Block is as below :-
DECLARE
TYPE RefCurTyp IS REF CURSOR;
alter_tbl VARCHAR2(200);
a_null CHAR(1);
tbl VARCHAR2(200);
clmn VARCHAR2(200);
dtyp VARCHAR2(200) ;
dlth VARCHAR2(200);
c RefCurTyp;
BEGIN
open c for 'select utc.table_name, utc.column_name, utc.data_type, utc.data_length FROM user_tab_columns utc, user_tables ut
WHERE utc.data_type = ''VARCHAR2'' AND utc.char_used =''B'' AND ut.table_name = utc.table_name';
LOOP
dbms_output.put_line(clmn);
FETCH c INTO tbl, clmn, dtyp, dlth;
EXIT WHEN c%NOTFOUND;
EXECUTE IMMEDIATE
'alter table '||tbl||' modify ('||clmn||' '||dtyp||'('||dlth||' CHAR))';
END LOOP;
CLOSE c;
END;
Even after pounding my head on it for 3 days i am unable to figure out the issue with this. Any input is appreciated.
While executing the same code via TOAD i get :-

You can use dbms_output to display the dynamic statement you are executing. To make sure you see and execute the same thing it's simpler to put the statement into a variable (you have one you aren't using). If you change the cursor type you don't need the local variables though, you can construct the statement as part of the cursor query, and then refer to it multiple times; you also won't have to escape your single quotes:
set serveroutput on
BEGIN
FOR r IN (
SELECT 'alter table ' || utc.table_name || ' modify (' || utc.column_name || ' '
|| utc.data_type || '(' || utc.data_length || ' CHAR))' as alter_stmt
FROM user_tab_columns utc
JOIN user_tables ut ON ut.table_name = utc.table_name
WHERE utc.data_type = 'VARCHAR2' AND utc.char_used ='B'
)
LOOP
dbms_output.put_line(r.alter_stmt);
execute immediate r.alter_stmt;
END LOOP;
END;
/
I suspect you have a table or column name that contains an invalid character and was created with a quoted identifier. That will probably be obvious from the output you see immediately before it fails. You can easily add double quotes to all of the identifiers by concatenating them as part of the statement generation:
BEGIN
FOR r IN (
SELECT 'alter table "' || utc.table_name || '" modify ("' || utc.column_name || '" '
|| utc.data_type || '(' || utc.data_length || ' CHAR))' as alter_stmt
FROM user_tab_columns utc
JOIN user_tables ut ON ut.table_name = utc.table_name
WHERE utc.data_type = 'VARCHAR2' AND utc.char_used ='B'
)
LOOP
dbms_output.put_line(r.alter_stmt);
execute immediate r.alter_stmt;
END LOOP;
END;
/
You said you're running this while connected as SYS, and you're looking at user_tables, so you are altering tables owned by SYS - which seems like a very bad idea. Even if you don't intend to modify built-in data dictionary tables, this will do so, and that would imply you've been creating your own objects in the SYS schema - which is generally considered a very bad idea. You should create a separate user account and only create and modify objects in that schema.
In my 11g instance SYS has a table that generates output from my first query as:
alter table _default_auditing_options_ modify (A VARCHAR2(1 CHAR));
... which would get ORA-00911 because of the underscores. If the identifiers are quoted then it would work:
alter table "_default_auditing_options_" modify ("A" VARCHAR2(1 CHAR));
... but, once again, you should not be altering built-in tables.

Related

snowflake procedure syntax error while querying information schema

Idea is I am trying to query information schema metadata and build columns based on datatypes if date then I create min date max date ,if number then check count of distinct or count rows
I have simplified now I will pass dbname , schema name and table name as parameters,
My output should return with below columns
SCHEMA_NM,TBL_NAME,_COUNT,_DISTINCT_COUNT,_MIN_DATE,_MAX_DATE.
For testing purpose at least if two measure is syntactically correct remaining I will take care
CREATE OR REPLACE PROCEDURE PROFILING( DB_NAME VARCHAR(16777216),TBL_SCHEMA VARCHAR(16777216), TBL_NAME VARCHAR(16777216))
RETURNS VARCHAR(16777216)
LANGUAGE SQL
EXECUTE AS CALLER
AS
$$
DECLARE
BEGIN
create or replace temporary table tbl_name as (select case when data_type=NUMBER then (execute immediate 'select count(col_val) from ' || :1 || '.' || :2 || '.' || :3) else null end as col_value_num,case when data_type=DATE then (execute immediate 'select count(col_val) from ' || :1 || '.' || :2 || '.' || :3) else null end as col_min_date from ':1.information_schema.columns where table_catalog=:1 and table_schema=:2 and table_name=:3 ' )) ;
insert into some_base_table as select * from tbl_name;
truncate tbl_name
RETURN 'SUCCESS';
END;
$$;
With above query I am getting below error
error : SQL compilation error: Invalid expression value (?SqlExecuteImmediateDynamic?) for assignment.
Any help here please
There are multiple errors in your code, but the error you mentioned is about this line:
num := (execute immediate "select count(col_val) from tab_cat.tab_schema.tab_name") ;
NUM is declared as integer, you can't directly assign a value from execute immediate. Execute immediate statement returns a resultset. To access the value you need to define a cursor and fetch the data.
The SQL should be surrounded by single quotes (not double quotes), and you should also build the string correctly. Something like this:
result := (execute immediate 'select count(col_val) from ' || tab_cat || '.' || tab_schema || '.' || tab_name ) ;

Invalid identifier in dynamic select

I have a little procedure which should truncate partition if exists
create or replace PROCEDURE drop_partition(p_table_name VARCHAR2, p_load_seq INTEGER)
AS
l_sql_text VARCHAR2(1000 CHAR);
l_part_exists NUMBER;
BEGIN
l_sql_text:= 'SELECT count(*) FROM user_tab_partitions where table_name=' || p_table_name ||' and high_value='||p_load_seq ;
EXECUTE IMMEDIATE l_sql_text INTO l_part_exists;
if (l_part_exists=1) THEN
l_sql_text := 'ALTER TABLE ' || p_table_name || ' DROP PARTITION FOR(' || to_char(p_load_seq) || ')';
EXECUTE IMMEDIATE l_sql_text;
END IF;
END;
If I try to run procedure like this
call drop_partition('test',1);
There is an error:
ORA-00904: "TEST": invalid identifier
ORA-06512: at "DROP_PARTITION", line 8
00904. 00000 - "%s: invalid identifier"
What is the problem and how can I fix it?
You need to enclose the table name in single quotes, which you'll need to escape:
... where table_name=''' || p_table_name ||''' and ...
i.e.:
l_sql_text:= 'SELECT count(*) FROM user_tab_partitions where table_name=''' || p_table_name ||''' and high_value='||p_load_seq ;
There is an alternative quoting mechanism that removes the need to escape the quotes but I think it would be more confusing here.
When using dynamic SQL it can be useful to output the generated statement for debugging purposes, before executing it:
dbms_output.put_line(l_sql_text);
and enabling output in your client before you make the call.
As Justin Cave quite rightly pointed out, the query part of your procedure doesn't need to be dynamic anyway; you can use static SQL:
SELECT count(*)
INTO l_part_exists
FROM user_tab_partitions
where table_name = p_table_name
and high_value = p_load_seq;
Identifiers like table names are uppercase in the data dictionary by default, so you could do upper(p_table_name) in that statement (static or dynamic); but in case you do have any mixed-case quoted identifiers you could instead rely on the caller passing the name in the correct case:
drop_partition('TEST', 1);
... assuming your test table doesn't have a quoted identifier.

COMMENT ON generates ORA-00905: missing keyword through EXECUTE IMMEDIATE

I need help on figuring out the problem with the ORA-00905: missing keyword ORA-06512: at line 73
When it says line 73 it actually refers to the sql statement itself at line 56. However, I am using this same script with a different table which working perfectly.
By changing the schema, table and column name I keep getting this error. I have be experimenting with several versions and also using fetch into cursor.
It keeps saying the sql statement has missing keyword but it is working on another script with the same line. I am hoping somebody could help me here. This is my first time posting on this forum and I am hoping someday I could contribute to this great community. Thank you in advance!
DECLARE
--CREATE OR REPLACE PROCEDURE setcomment
--IS
CURSOR cur IS
SELECT COLUMN_NAME, TABLE_NAME, OWNER
FROM DBA_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN'
ORDER BY OWNER ASC, TABLE_NAME ASC, COLUMN_NAME ASC;
c_schema_name DBA_TAB_COLUMNS.OWNER%type;
c_table_name DBA_TAB_COLUMNS.TABLE_NAME%type;
c_column_name DBA_TAB_COLUMNS.COLUMN_NAME%type;
--This is a variable name to concatenate column names from <c_schema_name>.<c_table_name>.<c_column_name>
col_name VARCHAR(250) ;
--This is a variable to hold SQL statement and the message to be commented
sql_stmt1 VARCHAR(2000) ;
msg VARCHAR(250) := ' '' Comment going here '' ';
BEGIN
--Looping r cursor through cur cursor. Retrieving a row of record at a time
FOR r in cur LOOP
c_schema_name := r.owner;
c_table_name := r.table_name;
c_column_name := r.column_name;
--Concatenate all the column names into a single column name.
col_name := c_schema_name||'.'||c_table_name||'.'||c_column_name;
sql_stmt1 := 'COMMENT ON COLUMN '|| col_name ||' IS ''Comment going here '' ' ;
-- sql_stmt1 := 'COMMENT ON COLUMN '|| col_name ||' IS '||msg;
EXECUTE IMMEDIATE sql_stmt1;
--EXECUTE IMMEDIATE 'COMMENT ON COLUMN '|| c_schema_name||'.'||c_table_name||'.'||c_column_name || ' IS '' Comment going here '' ' ;
DBMS_OUTPUT.PUT_LINE ('COMMENT ON ' || col_name || ' procedure completed....');
END LOOP;
END;
/
If you still cannot find a source of the error, then create a log table, run the below code, and display (select) all error entries from the table
Then try to manually run the command.
Does you user have an appriopriate privileges to comment on tables in other schemas ? It can have a privilege to SELECT from DBA_TAB_COLS, but that doesn't mean that it can modify other schemas/tables.
CREATE TABLE log_errors( error_msg varchar2(4000));
DECLARE
CURSOR cur IS
SELECT COLUMN_NAME, TABLE_NAME, OWNER
FROM DBA_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN'
ORDER BY OWNER ASC, TABLE_NAME ASC, COLUMN_NAME ASC;
col_name VARCHAR(250) ;
sql_stmt1 VARCHAR(2000) ;
msg VARCHAR(250) := 'Comment going here';
BEGIN
FOR r in cur LOOP
col_name := '"'|| r.OWNER ||'"."'||r.TABLE_NAME||'"."'||r.COLUMN_NAME||'"';
sql_stmt1 := 'COMMENT ON COLUMN ' || col_name || ' IS ''' || msg || '''';
BEGIN
EXECUTE IMMEDIATE sql_stmt1;
EXCEPTION
WHEN OTHERS THEN
INSERT INTO log_errors( error_msg ) VALUES ( sql_stmt1 );
END;
END LOOP;
END;
/
SELECT * FROM log_errors;
In addition to Mathguy's answer - your script will fail if any of the tables has been created using quoted identifiers
Database Object Naming Rules
Every database object has a name. In a SQL statement, you represent
the name of an object with a quoted identifier or a nonquoted
identifier.
A quoted identifier begins and ends with double quotation marks (").
If you name a schema object using a quoted identifier, then you must
use the double quotation marks whenever you refer to that object.
A nonquoted identifier is not surrounded by any punctuation.
You can use either quoted or nonquoted identifiers to name any
database object. However, database names, global database names, and
database link names are always case insensitive and are stored as
uppercase. If you specify such names as quoted identifiers, then the
quotation marks are silently ignored.
Simple practical example - a name of the first table is nonquoted identifier, a name of the second table is quoted identifier :
CREATE TABLE table_one (
SSAN int
);
CREATE TABLE "TaBle ##% TWO" (
SSAN int
);
SELECT 'COMMENT ON COLUMN ' || OWNER || '.' || TABLE_NAME || '.' || COLUMN_NAME || ' IS ''My superb comment'''
As my_comment_command
FROM ALL_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN' ;
MY_COMMENT_COMMAND
----------------------------------------------------------------
COMMENT ON COLUMN SCOTT.TABLE_ONE.SSAN IS 'My superb comment'
COMMENT ON COLUMN SCOTT.TaBle ##% TWO.SSAN IS 'My superb comment'
It's obvious, that the second command will fail.
But if you use quotes in your script, then everything will work fine:
SELECT 'COMMENT ON COLUMN "' || OWNER || '"."' || TABLE_NAME || '"."' || COLUMN_NAME || '" IS ''My superb comment'''
As my_comment_command
FROM ALL_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN' ;
MY_COMMENT_COMMAND
----------------------------------------------------------------------
COMMENT ON COLUMN "SCOTT"."TABLE_ONE"."SSAN" IS 'My superb comment'
COMMENT ON COLUMN "SCOTT"."TaBle ##% TWO"."SSAN" IS 'My superb comment'

Invalid identifier error in oracle procedure for database link

I have below procedure for which i am passing db link as a parameter and create dynamic sql statement. While executing the procedure i am getting error as ORA-00904: "DB_CONNECTION_NAME": invalid identifier. I have declared the variable but still i am getting this error.
CREATE OR REPLACE PROCEDURE "EXT_SDR_RECEIVED"(in_db_link IN VARCHAR2)
AS
last_sm_id NUMBER := 0;
last_capt_date DATE;
l_sql VARCHAR2(5000);
db_connection_name VARCHAR2(100);
BEGIN
SELECT db_link INTO db_connection_name
FROM rator_monitoring_configuration.db_connection
WHERE db_link = in_db_link;
--DELETE DATA FROM TEMP_SDR_RECEIVED
DELETE FROM temp_sdr_received WHERE create_date < SYSDATE - 7;
-- first retrieve the last id (of the newest record) which has been imported at last extraction
SELECT last_task_id INTO last_sm_id
FROM capturing WHERE db_table = 'TEMP_SDR_RECEIVED';
SELECT capturing_date INTO last_capt_date
FROM capturing WHERE db_table = 'TEMP_SDR_RECEIVED';
dbms_output.PUT_LINE('DB' || db_connection_name);
-- retrieve all new records from remote SDR_O2 table and insert it into TEMP_SDR_RECEIVED where ID is greater than LAST_SM_ID
l_sql := 'INSERT INTO TEMP_SDR_RECEIVED(ID,RATING_CODE,A_NUMBER,CREATE_DATE,VOUCHER_ATTEMPT_ID,RATOR_BRAND_ID,BRAND_ID,STATUS_DESCRIPTION,ACCOUNT_PAYMENT_ID,SUBSCRIPTION_ID,DB_LINK)
SELECT SD.ID,SD.RATING_CODE,SD.A_NUMBER,to_date(substr(SD.ID, 1, 8), ''YYYYMMDD''),VA.ID,VA.BRAND_ID,BR.BRAND_ID,VA.STATUS_DESCRIPTION,VA.ACCOUNT_PAYMENT_ID,VA.SUBSCRIPTION_ID,DB_CONNECTION_NAME
FROM SDR_O2#' || db_connection_name || ' SD
JOIN VOUCHER_ATTEMPT#' || db_connection_name || ' VA
ON SD.ID = VA.SDR_ID,
RATOR_MONITORING_CONFIGURATION.BRAND BR
WHERE VA.BRAND_ID IS NOT NULL
AND BR.RATOR_BRAND_ID = VA.BRAND_ID
AND SD.RATING_CODE=''VOUCHER''
AND VA.STATUS_DESCRIPTION = ''USSD voucher''
AND SD.ID > LAST_SM_ID';
EXECUTE IMMEDIATE l_sql;
END ext_sdr_received;
You are referencing DB_CONNECTION_NAME in the select part of your dynamic query (look after VA.SUBSCRIPTION_ID). Do any of your 3 tables have that column? I suspect not.
I suspect that you wanted to select the value in the DB_CONNECTION_NAME variable instead. To do that, change that last part of the SELECT in your dynamic query like this:
'...,VA.SUBSCRIPTION_ID, ''' || DB_CONNECTION_NAME || '''
...
You may also want to look into how execute immediate supports parameter binding, so that, where possible, you can avoid having to write this ugly string concatenation code.
Also, I notice you are mixing join notations. That's asking for trouble. Stick to ANSI JOIN syntax.

generic stored procedure in oracle

I want to write a PLSQL stored procedure that accepts a table name as argument. This table is source table. Now inside my procedure i want to manipulate the fields of that table.
EX: I want to insert the records of this source table into another target table whose name is XYZ_<source table name>. The column names for source and target tables are the same. But there may be some extra fields in target table. How do i do it? The order of column names is not same.
You will have to build the INSERT statement dynamically.
create or replace procedure gen_insert
(p_src_table in user_tables.table_name%type
, p_no_of_rows out pls_integer)
is
col_str varchar2(16000);
begin
for rec in ( select column_name
, column_id
from user_tab_columns
where table_name = p_src_table
order by column_id )
loop
if rec.column_id != 1 then
col_str := col_str || ',' || rec.column_name;
else
col_str := rec.column_name;
end if:
end loop;
execute immediate 'insert into xyz_' || p_src_table || '('
|| col_str || ')'
|| ' select ' || col_str
|| ' from ' || p_src_table;
p_no_of_rows := sql%rowcount;
end;
/
Obviously you may want to include some error handling and other improvements.
edit
Having edited your question I see you have a special requirement for naming the target table which was obscured by the SO formatting.
You can do this using Dynamic SQL. Here's a link with basic info on Oracle Dynamic SQL