Question

I'm trying to run this sql script in oracle and get an error

ORA-00984: column not allowed here.

declare
  tablename varchar2(200):='imagesroom';
  temp varchar2(50):='room_id';
  iid number:=1;
  dir varchar2(200):='imgdirroom';
  in_fname varchar2(100):='img1.jpg';
  obj ORDIMAGE;
  ctx RAW(64) := NULL;
begin
  execute immediate 'INSERT INTO '||tablename||'('||temp||',extension,icon) 
  VALUES(iid,'''',ORDImage(''FILE'',upper(dir),in_fname)) 
  RETURNING icon INTO obj';
end;
/   

ERROR at line 1:
ORA-00984: column not allowed here
ORA-06512: at line 10

This one works great

INSERT INTO imagesroom(room_id,extension,icon) 
VALUES(iid,'',ORDImage('FILE',upper(dir),in_fname)) 
RETURNING icon INTO obj;

What's wrong with it?

if I do it this way

execute immediate 'INSERT INTO :table(:type,extension,icon) 
VALUES(:id,'''',ORDImage(''FILE'',upper(:dir),:fname)) RETURNING icon INTO obj' 
using tablename,temp,iid,dir,in_fname;

then

ERROR at line 1:
ORA-00903: invalid table name
ORA-06512: at line 10

appear...

Was it helpful?

Solution

Firstly, why the second insert inside a PL/SQL block works and almost the same insert executed dynamically using execute immediate statement doesn't? Simply because of name resolution. When second insert, that insert statement which you've described as this one works great is being executed iid , dir and in_fname resolved to locally declared variables. And in case of the dynamically executed insert statement those iid , dir resolved to column names of the table you are trying to insert data into, and it's simply not allowed to put column names of a table you are trying to insert into in the value clause of an insert statement. Use bind variables.

Secondly, about returning into clause. You need two of them. One as part of a dynamically formed insert statement(dynamic returning into clause) and one for execute immediate statement(static returning into clause), in order to get returning by the insert statement values out. Or, one dynamic returning into clause and one OUT bind variable(in this case).

To that end, your anonymous pl/sql block might look like this:

declare
  tablename  varchar2(200) :='imagesroom';
  temp       varchar2(50)  :='room_id';
  iid        number        :=1;
  dir        varchar2(200) :='imgdirroom';
  in_fname   varchar2(100) :='img1.jpg';
  obj        ORDIMAGE;
  ctx        RAW(64)       := NULL; -- really unnecessary, NULL by default.
  l_instr    varchar2(4000);
begin

  l_instr := 'INSERT INTO ' || tablename || '('||temp||',extension,icon) 
                VALUES(:1,'''',ORDImage(''FILE'',upper(:2), :3)) 
              RETURNING icon INTO :4';

  execute immediate l_instr 
    using iid, dir, in_fname  returning into obj;

end;
/   

OTHER TIPS

The RETURNING INTO clause should be outside the dynamic DML statement:

execute immediate 'INSERT INTO '||tablename||'('||temp||',extension,icon) 
  VALUES(iid,'''',ORDImage(''FILE'',upper(dir),in_fname))'
RETURNING icon INTO obj;

There's an example at the bottom of this documentation site about EXECUTE IMMEDIATE

The iid, dir, in_fname and obj references inside the SQL string you're executing are literals, so they're being interpreted as column names. You should use bind variables (as shown in the 10g docs:

...
begin
  execute immediate 'INSERT INTO '||tablename||'('||temp||',extension,icon) 
  VALUES(:iid,'''',ORDImage(''FILE'',upper(:dir),:in_fname)) 
  RETURNING icon INTO :obj'
  USING iid, dir, in_fname, OUT obj;
end;
/   

You can add something like

  dbms_output.put_line(obj.in_fname);

... to verify that obj is populated.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top