Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Parthiban.

Asked: February 17, 2011 - 1:12 am UTC

Last updated: August 28, 2013 - 7:17 pm UTC

Version: 11.2

Viewed 10K+ times! This question is

You Asked

Hi Tom

I am getting an error while executing the OS commands through the external table preprocessor.
/appuser/parthiban > cat my_dir_list.pl
#!/bin/perl
@dir=`ls -l|grep ":"`;
for (@dir) {print (join ("|", (split /\s+/)), "\n")}

/appuser/parthiban > ./my_dir_list.pl
-rwxrwx---|1|appuser|appusergrp|386|Dec|3|12:42|max.pl
-rwxrwx---|1|appuser|appusergrp|418|Dec|3|12:45|max2.pl
-rwxrwx---|1|appuser|appusergrp|160|Dec|3|12:49|max3.pl
-rwxrwx---|1|appuser|appusergrp|88|Feb|17|07:21|my_dir_list.pl
-rw-rw----|1|appuser|appusergrp|160|Feb|14|11:54|my_emp.lst
-rwxrwx---|1|appuser|appusergrp|51|Feb|17|03:33|my_files.lst
drwx------|2|appuser|appusergrp|256|Feb|14|12:38|sec
-rw-rw----|1|appuser|appusergrp|77|Dec|3|12:09|tmpfile1.txt

CREATE TABLE my_dir_list (
"permission" varchar2(4000),
"x"          varchar2(4000),
"owner"      varchar2(4000),
"group"      varchar2(4000),
"size"       varchar2(4000),
"month"      varchar2(4000),
"date"       varchar2(4000),
"time"       varchar2(4000),
"name"       varchar2(4000))
ORGANIZATION EXTERNAL
(
   TYPE ORACLE_LOADER
   DEFAULT DIRECTORY parthiban_dir
   ACCESS PARAMETERS
   (
      RECORDS DELIMITED BY NEWLINE
      PREPROCESSOR parthiban_dir: 'my_dir_list.pl'
      FIELDS TERMINATED BY '|'
     )
   LOCATION ('my_dir_list.pl')
);

select * from my_dir_list;

ORA-29913: error in executing ODCIEXTTABLEFETCH callout
ORA-29400: data cartridge error
KUP-04095: preprocessor command /appuser/parthiban/my_dir_list.pl encountered error "sh: ls:  not found
sh: grep:  not found
"

It should be because that the "oracle" user doesn't have enough privileges to execute such commands.
(1) Am I right?
(2) How to grant such privileges to the "oracle" user?

REM I should have posted this in a Unix forum
Thanks for your help Tom.
Warm Regards.

and Tom said...

you are assuming what the environment is here.

You are relying on $PATH - which would be a really really bad idea - what if someone put a program named ls in a directory that was "earlier" in your path than you expected? We would run that 'trojan' ls program - and we would run it as "oracle"

do not rely on defaults
do not rely on $PATH
be explicit


$ which grep
/bin/grep

reference /bin/grep in your script NOT just grep.

same with ls.


This has nothing to do with permissions - this has everything to do with assuming your environment would be set - and it won't be. Set everything, be explicit.

Rating

  (5 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

KUP-04076:

Parthiban Nagarajan, February 21, 2011 - 11:37 pm UTC

Hi Tom
This is just the continuation of my question.
Yes. The PATH environment variable was the issue. Now, I am referring the programs with full path and hence its working fine. Thanks for your guidance.

And, now ...
Please refer the following script and its output.
alter table my_dir_list location ('my_dir_list.pl');

Table altered.

select count(*) from my_dir_list;

  COUNT(*)
----------
         1


1 row selected.

alter table my_dir_list location ('.');

Table altered.

select count(*) from my_dir_list;

  COUNT(*)
----------
        43


1 row selected.

alter table my_dir_list location ('..');

Table altered.

select count(*) from my_dir_list;

  COUNT(*)
----------
      1815


1 row selected.

alter table my_dir_list location ('/');

Table altered.

select count(*) from my_dir_list;
select count(*) from my_dir_list
*
Error at line 0
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-04076: file name cannot contain a path specification: /

Is Oracle raising this error KUP-04076 intentionally?
Why the location should not point to a directory?
But you can see that it is allowing directories in the form of (.) and (..) (But no other directories)
So, what does it mean?

Are they checking like INSTR(location, '/') <> 0 or INSTR(location, '\') <> 0? For example, the following things are also not working.
alter table my_dir_list location ('./my_dir_list.pl');
select count(*) from my_dir_list;
alter table my_dir_list location ('./.');
select count(*) from my_dir_list;

Thanks in advance, Tom.
Tom Kyte
February 23, 2011 - 9:36 am UTC

they must be looking for something that indicates "more than just a filename here".

technically "." is a file in the directory pointed to by the external table.
technically ".." is a file in the directory pointed to by the external table.

but "../my_dir_list.pl" is NOT a file in the directory pointed to by the external table, it is outside of that directory.

The file associated with an external table must be "in" that directory.

Why Preprocessing?

Parthiban Nagarajan, February 25, 2011 - 10:06 am UTC

Hi Tom

We have a preprocessing logic written in perl before loading the data through SQL*Loader into the database. I asked myself the question - "Why Preprocessing?" i.e. I thought of eliminating SQL*Loader in favor of the External Tables; converting the perl into pl/sql. But, there comes the problem ...

We got a set of CSV files - (fMain, fX, fY). We produce a new file (fTemp) from these three files - by enriching fMain with either fX or fY depending on the values in fMain(kind of lookups).

My Idea gone like this ...
1) Three external tables on fMain, fX, fY - call it as xtMain, xtX, xtY.
2) for i in (select * from xtMain) loop
      ... some other logic ...
      select ...
        from xtX
       where ... = i. ...;
      ... some other logic ...
      select ...
        from xtY
       where ... = i. ...;
      ... some other logic ...
      ... etc ...
      write-to-fTemp-file;
   end loop;

Then only I realised my stupidity. Each SELECT inside the loop has opened and closed the associated files and hence the performance is really poor. I am not able to convert into a single SQL too because of the complex logic out there.
3) #Existing Perl code skeleton ...
   my (%lookup_x, %lookup_y);
   build_loopkups();
   while(<fMain>){
     ...
     v_out[2] = $lookup{value-from-fMain}
     ...
     print fTemp, v_out;
   }

I am really confused in lookup. In perl, they are using associative array kind of stuff (hashes). But we can BULK COLLECT into a nested table not associative arrays, right? Could you help me in converting this perl preprocessing into pl/sql?
Tom Kyte
February 25, 2011 - 1:27 pm UTC

why are you not just joining?????? Why is there any procedural logic here - just join xtmain to xty and xtx, using an outer join if necessary - then some judicious CASE statments in the select list to transform anything you need.

A reader, February 25, 2011 - 3:56 pm UTC

Excellent work Tom

Need Answer

Andrew Krenitz, August 27, 2013 - 7:14 pm UTC

I have a valid sh file that I can execute from the directory that is being used by organization external table.

for i in `find . -name *.csv `;
do
echo "$i"
done > dir.lst
ACCESS PARAMETERS (
RECORDS DELIMITED BY NEWLINE
PREPROCESSOR sibulkimport:'dircsv.sh'
NOBADFILE
NODISCARDFILE
NOLOGFILE
FIELDS TERMINATED BY '/'
OPTIONALLY ENCLOSED BY '"'
MISSING FIELD VALUES ARE NULL
I get Execution (27: 1): ORA-29913: error in executing ODCIEXTTABLEFETCH callout
ORA-29400: data cartridge error
KUP-04095: preprocessor command /u04/utlfile/sibulkimport/dircsv.sh encountered error "/u04/utlfile/sibulkimport/dircsv.sh: line 16: find: No such file or directory

Tom Kyte
August 28, 2013 - 7:17 pm UTC

we all need answers


preprocessor commands are executed in the environment of the server process - which is not even remotely similar to YOUR environment.

make sure "oracle" (the software owner) - the account that is running the dedicated or shared server - has access to this directory, that the directory is on the server, that execute is set up for "oracle".

and you are running unqualified commands. you do realize that you are relying on YOUR path to find "find". And that the environment variable PATH might be really different. and that scripts you code should always reference EXPLICIT paths to prevent trojans or the wrong executables from being used.

you probably need to execute /bin/find - not just find - since we probably do not put something as generic as /bin in OUR path.



which is sort of exactly the original answer I gave above to the original question!!!!!!


do some debuging in your script, dump out the env, echo some things to a trace file, whatever.

Preprocessor Vs Load When

A reader, March 09, 2014 - 9:49 am UTC

Would it be faster to do the filtering using the perl/awk scripts than the "LOAD WHEN" clause in access parameter.