Invalid identifier in dynamic select - sql

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.

Related

How to pass table name as a parameter in update procedure in Oracle?

I am new to Oracle so please sorry the question that seems to be very easy for you.
I need to get the following procedure with UPDATE query with replace function
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
BEGIN
EXECUTE IMMEDIATE
'update ' || p_table ||
'set docnum = replace(docnum, :2, :3 )'
USING p_search, p_replace;
END;
This procedure removes all spaces.
But when I call it
BEGIN
proc_replace_space_1('cm_risk.fct_loans_temp', ' ', '');
END;
I've got the following error
SQL Error [971] [42000]: ORA-00971: missing SET keyword
ORA-06512: at "CM_RISK.PROC_REPLACE_SPACE_1", line 9
ORA-06512: at line 2
How can I modify my code to handle the problems?
Thank you.
Dynamic SQL is hard because it turns compilation errors into runtime errors. So I urge you to acquire the good habit of assembling your dynamic SQL as string variables which you can persist to a log table if you have such a thing (and if you don't it would be another good habit to acquire) or display using dbms_output.put_line.
So your procedure would look like this:
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
l_sql varchar2(32767);
BEGIN
l_sql := 'update ' || p_table ||
'set docnum = replace(docnum, :2, :3 )';
EXECUTE IMMEDIATE
l_stmt
USING p_search, p_replace;
exception
when others then
dbms_output.put_line(l_sql);
raise;
END;
This approach allows you to see the actual SQL your procedure executed. Probably you'll be able to spot the syntax error immediately (in this case it's the missing space between table name and set). Otherwise you can try to run the statement for yourself and see what the SQL compiler highlights.
NB: depending on your environment you may need to enable DBMS_OUTPUT before you can see the message.
You just need to add a space before set. Currently your table name is appended to set keyword and it is assuming it as a table name i.e MyTableSet
CREATE OR REPLACE PROCEDURE proc_replace_space_1
(
p_table user_tables.table_name%TYPE,
p_search IN varchar2,
p_replace IN varchar2
)
IS
BEGIN
EXECUTE IMMEDIATE
'update ' || p_table ||
' set docnum = replace(docnum, :2, :3 )'
USING p_search, p_replace;
END;

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'

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

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.

Dynamic sql compiled but can't execute

I am trying to display the duplicate records using dynamic sql(execute immediate). I am getting 'An identifier with more than 30 characters was specified' error. What am I doing wrong with the dynamic sql?
CREATE OR REPLACE PROCEDURE FIND_DUP(P_TABLE IN VARCHAR2, P_COLUMN IN VARCHAR2)
AS
stmt_txt varchar2(4000);
BEGIN
stmt_txt:= 'select'
||p_column
|| 'from'
||p_table
|| 'group by'
||p_column
||'having count(*)>1';
execute immediate stmt_txt;
end;
/
EXECUTE FIND_DUP('EMPLOYEES','FIRST_NAME');
You're missing some spaces in your query.
stmt_txt:= 'select '
||p_column
|| ' from '
||p_table
|| ' group by '
||p_column
||' having count(*)>1';
Without the spaces your query would end up as selectFIRST_NAMEfromEMPLOYEESgroup byFIRST_NAMEhaving count(*)>1, which to Oracle looks like an identifier with more than 30 characters.
Hi the first thing whih should be kept in mind while using dynamic sql is ALWAYS print the sql generated as it will give you the idea what query exactly you are running. This code might help you to solve your query as you have done almost everything right only you were missing with some spaces.
CREATE OR REPLACE
PROCEDURE FIND_DUP(
P_TABLE IN VARCHAR2,
P_COLUMN IN VARCHAR2)
AS
stmt_txt VARCHAR2(4000);
BEGIN
stmt_txt:= 'select' ||' ' ||p_column ||' ' ||'from' ||' ' ||p_table;
EXECUTE immediate stmt_txt;
DBMS_OUTPUT.PUT_LINE(STMT_TXT);
END;
For the output
BEGIN
FIND_DUP('DUAL','SYSTIMESTAMP');
END;
---------------------------------------------------------------------------
OUTPUT
select SYSTIMESTAMP from DUAL
Statement processed.
0.01 seconds
---------------------------------------------------------------------------

Strange ORA-06502: PL/SQL: numeric or value error

I'm looping through the table definitions in a database and extracting the DDL in order to be able to create 'golden data' sets of table duplicates for software testing purposes. I've got the below code which extracts the DDL just fine up to one table where it throws a ORA-06502 error. The thing is, I've defined the variable that the DDL is being returned to as a CLOB, which should be big enough. Here's the code:
create or replace
procedure gettabledescription as
current_date_time varchar2(32);
sql_statement varchar2(200);
ddl_return clob;
new_ddl clob;
BEGIN
DBMS_OUTPUT.ENABLE;
current_date_time:= to_char(sysdate,'MM/DD/YYYY - HH24:MI:SS');
dbms_output.put_line(current_date_time);
for cursor_rec in (select * from user_objects where object_type='TABLE' and object_name not like 'TM%' and object_name not like 'TZ_%' and object_name not like 'EXT_%' and object_name not like 'RPT_%' and object_name not like 'ETL_%') loop
sql_statement := 'select dbms_metadata.get_ddl(''TABLE'',' || '''' || cursor_rec.object_name || '''' || ') from dual';
execute immediate sql_statement into ddl_return;
**error thrown here** new_ddl := replace(ddl_return,TO_CHAR(cursor_rec.object_name),'QA_' || TO_CHAR(cursor_rec.object_name));
dbms_output.put_line(new_ddl);
end loop;
END GETTABLEDESCRIPTION;
I don't understand why I'm getting that error.
On Oracle 10gR2, When I call DBMS_OUTPUT.PUT_LINE with a varchar2 or clob longer than 8191 bytes, I receive an ORA-06502 error. The solution would be to loop through the clob, pulling out lines and calling put_line once for each.