CSVReader PL/SQL + java

  • support on Oracle 10, 11, 12

1. Create java source with name CSVReader.

CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "CSVReader" AS
import java.lang.*;
import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;

public class CSVReader {

// Procedure set encoding for Java = cp1251 (for situations where database character set is AL32UTF8), otherwise there not will be read Russian characters
public static void encod()
{
System.setProperty("file.encoding","cp1251");
}

// Procedure for filling the object type t_line (to display the data from csv file)
static void toLine (int pRowNum, String pValue, int pDelQoutes)
throws Exception {
encod();
#sql {
begin
csvreader.private_add_line (
p_row_nr        => :pRowNum,
p_value         => :pValue,
p_delqoutes     => :pDelQoutes );
end;
};
}

// Function replace all separator between double quotes
public static String replace_between_qoutes(String p_text, String p_sep)
throws Exception {
char sep = p_sep.charAt(0);
String copy = new String();
boolean inQuotes = false;
int code = (int) sep;
char dblq = '"';

for(int i=0; i<p_text.length();++i) {
if (p_text.charAt(i)=='"')
inQuotes = !inQuotes;

if (p_text.charAt(i)==sep && inQuotes)
copy += "chr("+code+")";

else
copy += p_text.charAt(i);
}
return copy;
}

// Procedure reading csv file from disk
public static void read(String p_file, Integer p_skip, Integer p_delqoutes)
throws Exception {
encod();
int             line_counter     = 1;
int             col_counter      = 0;
File            file        = new File(p_file);
BufferedReader  br          = null;
String          line        = "";
String          text        = "";

br = new BufferedReader(new FileReader(file));
// read line by line
while ((line = br.readLine()) != null) {
// skip lines when set to skiplines
if (line_counter > p_skip) {
// filling oracle type for display values
CSVReader.toLine(
line_counter,
line,
p_delqoutes
);
}
line_counter++;
}
}

};
/

2. Then create a package of “csvreader” for publication the Java call specification, so we can access it via PL/SQL.

CREATE OR REPLACE package csvreader as

/* Package for parse CSV file in Oracle */

g_sep       varchar2(10) := ',';

type t_line is record (
row_nr      number,
col_nr      number,
value       varchar2(4000));
type t_lines is table of t_line;

/* pipelined table function to return all cells of an CSV file */
function get_lines (
p_file         in varchar2,
p_sep          in varchar2 := ',',
p_skip         in number   := 0,
p_delqoutes    in number   := 1)
return t_lines
pipelined;

/* for filling type t_line from java code */
procedure private_add_line (
p_row_nr        in number,
p_value         in varchar2,
p_delqoutes     in number);

function replace_between_qoutes(
p_text          in varchar2,
p_sep           in varchar2
)
return Varchar2
is language java
name 'CSVReader.replace_between_qoutes(java.lang.String, java.lang.String) return java.lang.String';

end csvreader;
/

CREATE OR REPLACE package body csvreader as

g_lines t_lines;

procedure read (
    p_file          in varchar2,
    p_skip          in number,
    p_delqoutes     in number)
is language java
name 'CSVReader.read(java.lang.String, java.lang.Integer, java.lang.Integer)';

/* parse line by line of csv file */
function get_lines (
    p_file          in varchar2,
    p_sep           in varchar2 := ',',
    p_skip          in number   := 0,
    p_delqoutes     in number   := 1)
    return t_lines
    pipelined
is
begin
    g_lines := new t_lines();

    if p_sep != ',' then
        g_sep := p_sep;
    end if;

    if p_file is not null then
        read (
            p_file      => p_file,
            p_skip      => p_skip,
            p_delqoutes => p_delqoutes);
    end if;

    for i in 1 .. g_lines.count loop
        pipe row (g_lines(i));
    end loop;

    g_lines := null;
    return;
end get_lines;

/* filling type t_line */
procedure private_add_line (
    p_row_nr        in number,
    p_value         in varchar2,
    p_delqoutes     in number)
is
    l_row t_line;
    val varchar2(4000);
begin

-- divided line on the part depending of the separator
for rec in ( select replace(
                    regexp_substr(newstr,'[^'||g_sep||']+', 1, level),
                        'chr('||ascii(g_sep)||')', g_sep) text, level lvl
             from
                ( select str, listagg(rep,'') within group (order by n) newstr
                 from (
                     select str
                     ,n, case
                            when (mod(n,2)=q)
                                then '"'||replace(sub,g_sep,'chr('||ascii(g_sep)||')')||'"'
                            else sub
                         end rep
                     from (
                         select str,
                         m.column_value n,
                         case when regexp_like(str,'^"') then 1 else 0 end q,
                         regexp_substr(str,'[^"]+',1,m.column_value) sub
                         from  (select p_value str from dual) t,
                                table(cast(multiset(
                                    select level from dual
                                    connect by level <= regexp_count(str,'[^"]+')
                                ) as sys.odciNumberList)) m
                     )
                 )
                 group by str
                )
            connect by regexp_substr(newstr, '[^'||g_sep||']+', 1, level) is not null
           )
loop
    -- remove the double quotation marks at the beginning and end of the line
    val := case
                when p_delqoutes = 0 then rec.text
                else regexp_replace(rec.text, '^([^"]*"(.*)"[^"]*|(.*))$', '\2\3')
           end;

    l_row.row_nr        := p_row_nr;
    l_row.col_nr        := rec.lvl;
    l_row.value         := val;
    g_lines.extend;
    g_lines(g_lines.count) := l_row;
    
end loop;

end private_add_line;

end csvreader;
/

3. Grant the Oracle JVM the relevant filesystem permissions.

DECLARE
l_user VARCHAR2(30) := :user_name;
BEGIN
DBMS_JAVA.grant_permission(l_user, 'java.io.FilePermission','<<ALL FILES>>', 'read ,write, execute, delete');
DBMS_JAVA.grant_permission(l_user, 'SYS:java.lang.RuntimePermission','writeFileDescriptor', '');
DBMS_JAVA.grant_permission(l_user, 'SYS:java.lang.RuntimePermission','readFileDescriptor', '');
DBMS_JAVA.grant_permission(l_user, 'SYS:java.util.PropertyPermission','file.encoding', 'write' );
EXECUTE IMMEDIATE 'GRANT EXECUTE ON sys.odciNumberList to '||l_user;
END;
/

4. How use:

/*
Input parameters:
  p_file        - fullpath to file. The file must be located on the computer
                  where the database Oracle or a network path,
                  where access to the computer issued to Oracle;
  p_sep         - separator, default ",";
  p_skip        - skip n-lines before read, deafult 0;
  p_delqoutes   - remove the double quotation marks at the beginning
                  and end of the line (0 - no, 1 - yes), default 1.

Output:
  row_nr      number,         - number of line in file
  col_nr      number,         - number of column
  value       varchar2(4000)  - text

Example:
create file "c:\test.txt" with this content:
"1.0.0.0","1.0.0.255","16777216","16777471","AU","Australia, "Montenegro LCD", USA"
"1.0.1.0","1.0.3.255","16777472","16778239","CN","China"
"1.0.4.0","1.0.7.255","16778240","16779263","AU","Australia, USA"
"1.0.8.0","1.0.15.255","16779264","16781311","CN","China"
"1.0.16.0","1.0.31.255","16781312","16785407","JP","Japan"
"1.0.32.0","1.0.63.255","16785408","16793599","CN","China"
"1.0.64.0","1.0.127.255","16793600","16809983","JP","Japan"
"1.0.128.0","1.0.255.255","16809984","16842751","TH","Thailand, Siria"
*/

-- run this select:
select * from table(csvreader.get_lines(
p_file      => 'c:\test.txt',
p_sep       => ',',
p_skip      => 0,
p_delqoutes => 1));
---------------------------------------------
1    1    1.0.0.0
1    2    1.0.0.255
1    3    16777216
1    4    16777471
1    5    AU
1    6    Australia, "Montenegro LCD", USA
2    1    1.0.1.0
2    2    1.0.3.255
............................................
Реклама

Managing files and folders using Java in Oracle PL/SQL

Collection of Java utilities for work with files anf folders in Oracle PL/SQL.
For testing I used Java 6 and Oracle 11.2.0.1.

1. Create a number of important facilities for testing and the work of our utilities.

CREATE TABLE BLOB_TAB
( F_ID    INTEGER,
  F_NAME  VARCHAR2(128 BYTE),
  F_BLOB  BLOB,
  F_LEN   VARCHAR2(20 BYTE) );

CREATE SEQUENCE BLOB_TAB_SEQ
  START WITH 1
  MAXVALUE 9999999
  MINVALUE 1
  NOCYCLE
  CACHE 5
  ORDER;

CREATE GLOBAL TEMPORARY TABLE ATTR_FILES
( DIRNAME   VARCHAR2(255 BYTE),
  FILENAME  VARCHAR2(255 BYTE),
  LENGTH    NUMBER,
  MOD_DATE  DATE,
  TYPE      VARCHAR2(1 BYTE) )
ON COMMIT PRESERVE ROWS;

CREATE OR REPLACE TYPE LIST_FILES AS TABLE OF VARCHAR2(255);

2. Create Java source named «Lobster».

CREATE OR REPLACE AND RESOLVE JAVA SOURCE NAMED "Lobster" as
import java.io.*;
import java.sql.*;
import oracle.sql.BLOB;
import oracle.sql.CLOB;
import oracle.jdbc.OracleTypes;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.CallableStatement;
import java.text.SimpleDateFormat;
import java.util.Date;
import oracle.sql.driver.*;
import oracle.sql.ArrayDescriptor;
import oracle.sql.ARRAY;

// What's: Collection java utilities for work with files, folders, CLOB and BLOB.
// Author: souluran adn Denis Popov (http://www.sql.ru/faq/faq_topic.aspx?fid=469)

public class Lobster {

// Procedure set encoding for Java = cp1251 (for situations where database character set is AL32UTF8), otherwise there not will be read Russian characters
  public static void encod()
  {
    System.setProperty("file.encoding","cp1251");
  }

// Procedure get list of file names in directory
  public static ARRAY list_files(String dirname)
  throws SQLException {
    encod();
    Connection con = null;
    ARRAY listed = null;
    con = getConnection();
    File dir = new File(dirname);
    try{
        if (dir.exists()) {
            ArrayDescriptor arrayDescriptor = new ArrayDescriptor("LIST_FILES",con);
            listed = new ARRAY(arrayDescriptor,con,((Object[])dir.list()));
        }
    } finally {
    if (con != null) {con.close();}
      }
  return listed;
  }

// Procedure get list of attributes files in directory
  public static void list_files_attr(String dirname)
  throws SQLException {
     encod();
     File path = new File(dirname);

     if (path.exists()) {

        String[] list = path.list();
        String element;

        File fileObject;
        long length;
        String dateStr, type;
        Date now = new Date();
        SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy H:m:s");

        for(int i = 0; i < list.length; i++)
        {
            element = list[i];
            fileObject = new File(dirname + File.separatorChar + list[i]);
            length = fileObject.length();
            if (fileObject.isDirectory()) {
              type = "d";
            } else if (fileObject.isFile()) {
              type = "f";
            } else {
              type = "?";
            }

            java.util.Date d = new java.util.Date(fileObject.lastModified());
            dateStr = format.format(d);

            #sql { INSERT INTO ATTR_FILES (dirname, filename, length, mod_date, type)
                   VALUES (:dirname, :element, :length, to_date(:dateStr, 'dd.mm.yyyy hh24:mi:ss'), :type) };
        }
     }
  }

// Function get modified time file
  public static String getfiletime(String dirname, String fileName)
  throws SQLException {
     encod();
     File path = new File(dirname + "\\" + fileName);
     String dateStr = null;
     if (path.exists()) {
        Date now = new Date();
        SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy H:m:s");
        java.util.Date d = new java.util.Date(path.lastModified());
        dateStr = format.format(d);
     }
     return dateStr;
  }

// Function check exixsting file in directory
  public static oracle.sql.NUMBER existsfile(String dirname, String fileName) throws Exception {
    //System.setProperty("file.encoding","cp1251");
    encod();
    int res = 0;
    File theDir = new File(dirname + "\\" +fileName);
    if (!theDir.exists()) {
        res = 0;
     }
    else {
        res = 1;
     }
   return new oracle.sql.NUMBER(res);
  }

// Procedure move file
  public static void movefile(String dirname, String fileName, String newdirname, String newfileName)
  throws IOException {
    encod();
    //String myfile = new String(fileName.getBytes("windows-1251"), "windows-1251");
    File srcFile = new File(dirname + "\\" + fileName);
    File destFile = new File(newdirname + "\\" + newfileName);
    InputStream input = null;
    OutputStream output = null;
    try {
        input = new FileInputStream(srcFile);
        output = new FileOutputStream(destFile);
        byte[] buf = new byte[1024];
        int bytesRead;
        while ((bytesRead = input.read(buf)) > 0) {
        output.write(buf, 0, bytesRead);
        }
    } finally {
        input.close();
        output.close();
        srcFile.delete();
    }
  }

// Procedure copy file
  public static void copyfile(String dirname, String fileName, String newdirname, String newfileName)
  throws Exception, IOException {
    encod();
    File srcFile = new File(dirname + "\\" + fileName);
    File destFile = new File(newdirname + "\\" + newfileName);
    InputStream input = null;
    OutputStream output = null;
    try {
        input = new FileInputStream(srcFile);
        output = new FileOutputStream(destFile);
        byte[] buf = new byte[1024];
        int bytesRead;
        while ((bytesRead = input.read(buf)) > 0) {
        output.write(buf, 0, bytesRead);
        }
    } finally {
        input.close();
        output.close();
    }

  }

// Procedure delete file
  public static void deletefile(String dirname, String fileName)
  throws IOException {
    System.setProperty("file.encoding","cp1251");
    try {
        File srcFile = new File(dirname + "\\" + fileName);
        srcFile.delete();
    } finally {
    }
  }

// Procedure create new directory in Windows
  public static void newdir(String dirname)
  throws Exception {
    encod();
    File theDir = new File(dirname);
    // if the directory does not exist, create it
    if (!theDir.exists()) {
    //System.out.println("creating directory: " + dirname);
    boolean result = false;
        try{
            theDir.mkdir();
            result = true;
            }
        catch(SecurityException se){
            //handle it
            }
    if(result) {
               //System.out.println(dirname + " created");
               }
                          }
  }

// Function check exixsting directory in Windows
  public static oracle.sql.NUMBER existsdir(String dirname) throws Exception {
    encod();
    int res = 0;
    File theDir = new File(dirname);
    if (!theDir.exists()) {
        res = 0;
     }
    else {
        res = 1;
     }
   return new oracle.sql.NUMBER(res);
  }

// Procedure recursive search files in directory
  public static void recursivecopy(File src, File dest)
  throws Exception, IOException  {
    encod();
        if(src.isDirectory()){

            //if directory not exists, create it
            if(!dest.exists()){
               dest.mkdir();
               System.out.println("Directory copied from "
                              + src + "  to " + dest);
            }

            //list all the directory contents
            String files[] = src.list();

            for (String file : files) {
               //construct the src and dest file structure
               File srcFile = new File(src, file);
               File destFile = new File(dest, file);
               //recursive copy
               recursivecopy(srcFile, destFile);
            }

        }else{
            //if file, then copy it
            //Use bytes stream to support all file types
            InputStream in = new FileInputStream(src);
                OutputStream out = new FileOutputStream(dest);

                byte[] buffer = new byte[1024];

                int length;
                //copy the file content in bytes
                while ((length = in.read(buffer)) > 0){
                   out.write(buffer, 0, length);
                }

                in.close();
                out.close();
                System.out.println("File copied from " + src + " to " + dest);
        }
  }

// Procedure copy directory in Windows
  public static void copydir(String srcDir, String destDir) throws Exception, IOException {
        encod();
        File src = new File(srcDir);
        File dest = new File(destDir);
        recursivecopy(src, dest);
  }

// Procedure recursive search in directory
  private static void recursiveDelete(File file) {
      System.setProperty("file.encoding","cp1251");
         if (!file.exists()) return;
         if (file.isDirectory()) {
            for (File f : file.listFiles()) recursiveDelete(f);
            file.delete();
         } else {
            file.delete();
         }
  }

// Procedure delete directory with all contetns in Windows
  public static void deletedir(String dirname) throws IOException  {
         encod();
         File directory = new File(dirname);
         recursiveDelete(directory);
  }

// Function import file into CLOB
  public static CLOB file2clob(String dirname, String fileName)
    throws SQLException, IOException {
    Connection con = null;
    CLOB clob = null;
    Writer writer = null;
    InputStream in = null;
    try {
      encod();
      con = getConnection();
      // For Oracle 9i and above.
      clob = CLOB.createTemporary(con, true, CLOB.DURATION_SESSION);
      // For Oracle 8i.
//      clob = createTemporaryClob(con);
      writer = clob.getCharacterOutputStream();
      in = new BufferedInputStream(new FileInputStream(new File(dirname + "\\" + fileName)));
      byte[] buf = new byte[clob.getChunkSize()];
      int length;
      while ((length = in.read(buf)) != -1) {
        writer.write(new String(buf, 0, length));
      }
    } finally {
      if (in != null) {in.close();}
      if (writer != null) {writer.close();}
      if (con != null) {con.close();}
    }
    return clob;
  }

// Function import file with characterSet into CLOB
  public static CLOB file2clob(String dirname, String fileName, String characterSet)
    throws SQLException, IOException {
    Connection con = null;
    CLOB clob = null;
    Writer writer = null;
    Reader reader = null;
    try {
      encod();
      con = getConnection();
      // For Oracle 9i and above.
      clob = CLOB.createTemporary(con, true, CLOB.DURATION_SESSION);
      // For Oracle 8i.
//      clob = createTemporaryClob(con);
      writer = clob.getCharacterOutputStream();
      reader = new InputStreamReader(
        new BufferedInputStream(new FileInputStream(new File(dirname + "\\" + fileName))), characterSet
      );
      char[] chars = new char[clob.getChunkSize()];
      int iChar;
      while ((iChar = reader.read(chars)) != -1) {
        writer.write(chars, 0, iChar);
      }
    } finally {
      if (reader != null) {reader.close();}
      if (writer != null) {writer.close();}
      if (con != null) {con.close();}
    }
    return clob;
  }

// Procedure export CLOB into file
  public static void clob2file(CLOB clob, String dirname, String fileName)
    throws SQLException, IOException {
    Writer writer = null;
    Reader reader = null;
    try {
      encod();
      writer = new BufferedWriter(new FileWriter(dirname + "\\" + fileName));
      reader = new BufferedReader(clob.getCharacterStream());
      int length;
      char[] buf = new char[clob.getChunkSize()];
      while ((length = reader.read(buf, 0, clob.getChunkSize())) != -1) {
        writer.write(buf, 0, length);
      }
    } finally {
      if (writer != null) {writer.close();}
      if (reader != null) {reader.close();}
    }
  }

// Procedure export CLOB into file with characterSet
  public static void clob2file(CLOB clob, String dirname, String fileName, String characterSet)
    throws SQLException, IOException {
    Writer writer = null;
    Reader reader = null;
    try {
      encod();
      writer = new OutputStreamWriter(
        new BufferedOutputStream(new FileOutputStream(new File(dirname + "\\" + fileName))), characterSet
      );
      reader = new BufferedReader(clob.getCharacterStream());
      int length;
      char[] buf = new char[clob.getChunkSize()];
      while ((length = reader.read(buf, 0, clob.getChunkSize())) != -1) {
        writer.write(buf, 0, length);
      }
    } finally {
      if (writer != null) {writer.close();}
      if (reader != null) {reader.close();}
    }
  }

// Function import file into BLOB
  public static BLOB file2blob(String dirname, String fileName)
    throws SQLException, IOException {
    Connection con = null;
    BLOB blob = null;
    OutputStream out = null;
    InputStream in = null;
    //Locale.setDefault(new Locale("ru", "RUS", "WIN"));
    try {
      encod();
      con = getConnection();
      // For Oracle 9i and above.
      blob = BLOB.createTemporary(con, true, BLOB.DURATION_SESSION);
      // For Oracle 8i.
//      blob = createTemporaryBlob(con);
      out = blob.getBinaryOutputStream();
      String myfile = null;
      in = new BufferedInputStream(new FileInputStream(new File(dirname + "\\" + fileName)));
      int length;
      int chunkSize = blob.getChunkSize();
      byte[] buf = new byte[chunkSize];
      while ((length = in.read(buf)) != -1) {
        out.write(buf, 0, length);
      }
    } finally {
      if (in != null) {in.close();}
      if (out != null) {out.close();}
      if (con != null) {con.close();}
    }
    return blob;
  }

// Procedure export BLOB into file
  public static void blob2file(BLOB blob, String dirname, String fileName)
    throws SQLException, IOException {
    InputStream in = null;
    OutputStream out = null;
    try {
      encod();
      out = new BufferedOutputStream(new FileOutputStream(new File(dirname + "\\" + fileName)));
      in = blob.getBinaryStream();
      int length;
      byte[] buf = new byte[blob.getChunkSize()];
      while ((length = in.read(buf)) != -1) {
        out.write(buf, 0, length);
      }
    } finally {
      if (in != null) {in.close();}
      if (out != null) {out.close();}
    }
  }

// Function connection
  public static Connection getConnection()
    throws SQLException {
    return DriverManager.getConnection("jdbc:default:connection:");
  }

// Function create temporary CLOB
  public static CLOB createTemporaryClob(Connection con)
    throws SQLException {
    CallableStatement cst = null;
    try {
      cst = con.prepareCall("{call dbms_lob.createTemporary(?, false, dbms_lob.SESSION)}");
      cst.registerOutParameter(1, OracleTypes.CLOB);
      cst.execute();
      return (CLOB)cst.getClob(1);
    } finally {
      if (cst != null) { cst.close(); }
    }
  }

// Function create temporary BLOB
  public static BLOB createTemporaryBlob(Connection con)
    throws SQLException {
    CallableStatement cst = null;
    try {
      cst = con.prepareCall("{call dbms_lob.createTemporary(?, false, dbms_lob.SESSION)}");
      cst.registerOutParameter(1, OracleTypes.BLOB);
      cst.execute();
      return (BLOB)cst.getBlob(1);
    } finally {
      if (cst != null) { cst.close();
        }
    }
  }

}
/

3. Then create a package of «Lobster» for publication the Java call specification, so we can access it via PL/SQL.

CREATE OR REPLACE PACKAGE Lobster as

/* Package of utilities for work with files, folders, CLOB and BLOB */

/* get list of files from directory */
function list_files(p_dir_name VARCHAR2) RETURN list_files AS
language java name 'Lobster.list_files(java.lang.String) return oracle.sql.ARRAY';

/* get list of attributes files from directory */
procedure list_files_attr(p_dir_name varchar2) as
language java name 'Lobster.list_files_attr(java.lang.String)';

/* file exists or not */
function existsfile(p_dir_name varchar2, p_file_name varchar2) return number as
language java name 'Lobster.existsfile(java.lang.String, java.lang.String) return oracle.sql.NUMBER';

/* move file to another directory */
procedure movefile(p_dir_name varchar2, p_file_name varchar2, p_newdir_name varchar2, p_newfile_name varchar2) as
language java name 'Lobster.movefile(java.lang.String, java.lang.String, java.lang.String, java.lang.String)';

/* copy file to another directory */
procedure copyfile(p_dir_name varchar2, p_file_name varchar2, p_newdir_name varchar2, p_newfile_name varchar2) as
language java name 'Lobster.copyfile(java.lang.String, java.lang.String, java.lang.String, java.lang.String)';

/* delete file from directory */
procedure deletefile (p_dir_name varchar2, p_file_name varchar2) as
language java name 'Lobster.deletefile(java.lang.String, java.lang.String)';

/* create new directory or subdirectory */
procedure newdir(p_dir_name varchar2) as
language java name 'Lobster.newdir(java.lang.String)';

/* directory exists or not */
function existsdir(p_dir_name varchar2) return number as
language java name 'Lobster.existsdir(java.lang.String) return oracle.sql.NUMBER';

/* delete directory or subdirectory with all contents */
procedure deletedir(p_dir_name varchar2) as
language java name 'Lobster.deletedir(java.lang.String)';

/* copy all contents from directory to another directory */
procedure copydir(p_dir_name varchar2, p_newdir_name varchar2) as
language java name 'Lobster.copydir(java.lang.String, java.lang.String)';

/* import file into CLOB */
function file2clob(p_dir_name varchar2, p_file_name varchar2) return clob as
language java name 'Lobster.file2clob(java.lang.String, java.lang.String) return oracle.sql.CLOB';

/* import file into CLOB with encoding */
function file2clob(p_dir_name varchar2, p_file_name varchar2, p_characterset varchar2) return clob as
language java name 'Lobster.file2clob(java.lang.String, java.lang.String, java.lang.String) return oracle.sql.CLOB';

/* import CLOB into file */
procedure clob2file(p_clob clob, p_dir_name varchar2, p_file_name varchar2) as
language java name 'Lobster.clob2file(oracle.sql.CLOB, java.lang.String, java.lang.String)';

/* import CLOB into file with encoding */
procedure clob2file(p_clob clob, p_dir_name varchar2, p_file_name varchar2, p_characterset varchar2) as
language java name 'Lobster.clob2file(oracle.sql.CLOB, java.lang.String, java.lang.String, java.lang.String)';

/* import file into BLOB */
function file2blob(p_dir_name varchar2, p_file_name varchar2) return blob as
language java name 'Lobster.file2blob(java.lang.String, java.lang.String) return oracle.sql.BLOB';

/* import BLOB into file */
procedure blob2file(p_blob blob, p_dir_name varchar2, p_file_name varchar2) as
language java name 'Lobster.blob2file(oracle.sql.BLOB, java.lang.String, java.lang.String)';

end;
/ 

4. Grant the Oracle JVM the relevant filesystem permissions.

DECLARE
  l_schema VARCHAR2(30) := 'USERNAME';
BEGIN
  DBMS_JAVA.grant_permission(l_schema, 'java.io.FilePermission', '<<ALL FILES>>', 'read ,write, execute, delete');
  DBMS_JAVA.grant_permission(l_schema, 'SYS:java.lang.RuntimePermission', 'writeFileDescriptor', '');
  DBMS_JAVA.grant_permission(l_schema, 'SYS:java.lang.RuntimePermission', 'readFileDescriptor', '');
  DBMS_JAVA.grant_permission(l_schema, 'SYS:java.util.PropertyPermission', 'file.encoding', 'write' );
END;
/

5. Examples how use package Lobster:

-- import file into BLOB
DECLARE

l_id INT DEFAULT 1;
l_dir VARCHAR2(128);
l_name VARCHAR2(128);
l_blob BLOB;

BEGIN
l_dir := 'd:\mydir\test';
l_name := 'my file.xls';

select BLOB_TAB_SEQ.NEXTVAL into l_id from dual;

INSERT INTO blob_tab
VALUES(l_id, null, EMPTY_BLOB(), 0);

SELECT f_blob INTO l_blob
FROM blob_tab
WHERE f_id = l_id
FOR UPDATE;

l_blob := lobster.file2blob(l_dir, l_name);

UPDATE blob_tab
SET f_name = l_name, f_blob = l_blob, f_len = dbms_lob.getlength(l_blob)
where f_id = l_id;
COMMIT;

END;
/

-- import BLOB into file
DECLARE
l_dir VARCHAR2(128);
l_name VARCHAR2(128);
l_blob BLOB;

BEGIN
l_dir := 'd:\mydir\test';
l_name := 'my file2.xls';

SELECT f_blob INTO l_blob
FROM blob_tab
WHERE f_id = :id;

lobster.blob2file(l_blob, l_dir, l_name);

END;
/

-- list of file name
SELECT column_value FROM TABLE(lobster.list_files('d:\mydir'));

--list of file attributes
begin
execute immediate 'truncate table ATTR_FILES';
lobster.list_files_attr('d:\mydir');
end;
/
select * from ATTR_FILES;

-- manage file and folders
declare
res number;
begin
-- copy file to another folder
lobster.copyfile('d:\mydir', 'test.txt', 'd:\my', 'test2.txt');
-- move file to another folder
lobster.movefile('d:\mydir', 'test.txt', 'd:\my', 'test.txt');
-- delete file
lobster.deletefile('d:\my', 'test.txt');
-- create new folder
lobster.newdir('d:\my5');
-- delete folder
lobster.deletedir('d:\my5');
-- copy all contetnts from folder to anothe folder
lobster.copydir('d:\my', 'd:\mytest');
-- exists file
res := lobster.existsfile('d:\my', 'test.txt');
dbms_output.put_line('file is ' || res);
res := lobster.existsdir('d:\my');
dbms_output.put_line('folder is ' || res);
end;
/ 

What encoding string UTF8 or WIN1251

Пример, как определить кодировку строки UTF8 или WIN1251?

with t as
(select 'Текст в кодировке UTF8' filename from dual union
select 'Текст в кодировке WIN1251' filename from dual union
select 'Text does not contain cyrilic charcters' filename from dual)
select filename,
case
    when not regexp_like(asciistr(convert(filename, 'cl8mswin1251')), '\FFFD') then
        case
            when regexp_like(convert(filename, 'cl8mswin1251', 'utf8'),'[а-яА-Я]') then 'UTF8'
            else 'WIN1251'
        end
        else 'WIN1251'
end ENCODE from t
order by 2;

FILENAME                                  ENCODE
----------------------------------------- -------
Текст РІ РєРѕРґРёСЂРѕРІРєРµ UTF8     UTF8   
Текст в кодировке WIN1251                 WIN1251
Text does not contain cyrilic charcters   WIN1251

Теперь дополним пример конвертацией текста UTF8 в WIN1251:

with t as
(select 'Текст в кодировке UTF8' filename from dual union
select 'Текст в кодировке WIN1251' from dual union
select 'Text does not contain cyrilic charcters' from dual)
select
encode,
case
    when ENCODE = 'WIN1251' then filename
    else convert(filename, 'cl8mswin1251', 'utf8')
end decode_filename
from(
select filename,
case
    when not regexp_like(asciistr(convert(filename, 'cl8mswin1251')), '\FFFD') then
        case
            when regexp_like(convert(filename, 'cl8mswin1251', 'utf8'),'[а-яА-Я]') then 'UTF8'
            else 'WIN1251'
        end
        else 'WIN1251'
end ENCODE from t)
order by 2;
                                      
ENCODE    DECODE_FILENAME
--------  -----------------------------------------
UTF8      Текст в кодировке UTF8
WIN1251   Текст в кодировке WIN1251
WIN1251   Text does not contain cyrilic charcters                                                      

 

 

Convert mail message from utf8 to windows-1251

Если вы с помощью PL\SQL делаете разбор почтовых сообщений (стандарта MIME) и получаете атрибуты (тема, тело сообщения, имена вложенных файлов и т.п.), Вы можете столкнуться с ситуацией, когда значение некоторых атрибутов дает подобный результат:

=?utf-8?B?cGxhbV/QutCw0Lot0YLQviDRgtCw0LpfMi5qcGc=?=

В моем случае я получал имя файла вложенного в письмо. Данное письмо я считывал из почтового ящика при помощи пакета MAIL_CLIENT.
Как оказалось когда имя файла во вложении было на латинице, то я его получал без проблем, но когда в имени файла содержались символы кириллицы, он кодировался в формат заголовка MIME с кодировкой UTF8.У Oracle для этих случаев есть пакет UTL_ENCODE, который позволяет кодировать и декодировать данные для почтовых сообщений.
В итоге написав следующую конструкцию, я решил проблему с кириллицей в имени файла:

file_name := case
when filename like '=?utf-8?%' then utl_encode.mimeheader_decode(filename)
else filename
end;

где, filename - это значение атрибута - имя вложенного файла в письме.
А условие case нужно для того, чтобы выполнять декодирование строк формата заголовка MIME в UTF8,
и не обрабатывать их, если имя содержит только латиницу.

В итоге я получил вместо:
=?utf-8?B?cGxhbV/QutCw0Lot0YLQviDRgtCw0LpfMi5qcGc=?=
нужное мне значение имени файла:
plam_как-то так_2.jpg

Для проверки:

select utl_encode.mimeheader_decode('=?utf-8?B?cGxhbV/QutCw0Lot0YLQviDRgtCw0LpfMi5qcGc=?=')
VAL from dual;

VAL                                                                             
----------------------
plam_как-то так_2.jpg  

Unwrap PL/SQL (2)

Thank you, good post.

emartife's Oracle Blog

En el post anterior colgué un ejecutable (Win 64 bits) que permite hacer unwrap del código PL/SQL. El hecho de que sea un ejecutable Windows puede ser incómodo a veces, así que si tuvieramos un código PL/SQL para hacerlo sería mucho más facil. Aquí adjunto el código necesario para hacerlo directamente desde la base de datos. Esta función nos devolverá un CLOB con el código original para que hagamos lo que queramos con él.

Clase de utilidad java para comprimir y descomprimir:

Package PL/SQL:

Espero que os sirva de ayuda para conocer más a fondo qué es lo que hace Oracle en sus packages…

View original post

PL/SQL пакет для генерации PDF

Oracle mechanics

Пакет от Anton Scheffer для создания PDF версии 1.3, с поддержкой TruType Fonts и вставкой картинок JPG и PNG

Код открытый, работает как описано, позиционируется как замена Middleware/Reports. Судя по публикации на сайте фирмы, с лицензионной чистотой должно быть всё норм

Входит в библиотеку PL/SQL утилит Alexandria для разных текстовых форматов и протоколов

View original post

Load data from file OS (txt, csv) using UTL_FILE

Написал процедуру LOADFROMFILE используя функционал пакета UTL_FILE, позволялющий быстро и практично загружать данные из текстового файла ОС (txt, csv) с разделителями (delimeters) в указанную таблицу. Процедура имеет несколько важных проверок, которые сократят ваше время, например, она сама считает количество столбцов в файле и сравнивает с количеством столбцов в таблице; конвертирует записи в тип DATE, где это нужно; возвращает сообщения об ошибках связанных с неверностью входных параметров, отсутствия файла и т.п.

 

CREATE OR REPLACE PROCEDURE LOADFROMFILE(dir_name VARCHAR2, file_name VARCHAR2, owner_name VARCHAR2, tab_name VARCHAR2, delim CHAR, exception_msg out VARCHAR2)
AS
/*
dir_name - Oracle директория расположения файлов
file_name - имя текстового файла
owner_name - имя владельца таблицы БД Oracle
tab_name - имя таблицы БД Oracle
delim - разделитель (delimeter) данных в файле (например, ';')
exception_msg - это возвращаемое сообщение о состоянии выполнения процедуры, позволяющзая понять, где мы допустили ошибку
*/
V_LINE CLOB;
F1 UTL_FILE.FILE_TYPE;
i INT;
colmax INT;
DATA_TYPE VARCHAR2(50);
TYPE arr_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER;
arr arr_table;
STRCOM CLOB;
str CLOB;
tabnamechek INT;
cnt INT DEFAULT 0;
csvfields INT;

BEGIN

    -- получаем количество столбцов в таблице
    select nvl((SELECT count(*) 
    FROM all_tables t, all_tab_columns c 
    WHERE t.owner=upper(owner_name) AND T.TABLE_NAME = C.TABLE_NAME AND t.TABLE_NAME = upper(tab_name) 
    GROUP BY t.TABLE_NAME),0) INTO colmax from dual;

  IF colmax > 0 THEN
    -- открываем файл для чтения
    F1 := UTL_FILE.FOPEN(dir_name, file_name, 'R');

    -- проверяем открылся ли файл
    IF UTL_FILE.IS_OPEN(F1) THEN
       exception_msg:='File '|| file_name ||' is Open';
    ELSE
       exception_msg:='File '|| file_name ||' is not Open';
    END IF;
    -- считываем построчно записи из файла
    LOOP
        cnt:=cnt+1;

       BEGIN
            UTL_FILE.GET_LINE(F1,V_LINE);
            -- получаем количество столбцов в одной строке файла
            csvfields:=regexp_count(regexp_replace(V_LINE,'.*?'),''||delim||'') + 1;
            -- проверяем совпадает ли количество столбцов таблицы и файле
            IF csvfields = colmax THEN
                -- получаем запись для каждого столбца строки
               FOR i IN 1..csvfields
               LOOP
                    -- заносим записи N-строки в массив
                    arr(i):=trim(REGEXP_SUBSTR(V_LINE, '[^'||delim||']+', 1, i));
                    
                    SELECT C.DATA_TYPE INTO DATA_TYPE
                    FROM all_tables t, all_tab_columns c 
                    WHERE t.owner=upper(owner_name) AND T.TABLE_NAME = C.TABLE_NAME AND t.TABLE_NAME = upper(tab_name)
                    AND C.COLUMN_ID = i;
                        
                    -- составляем часть кода динамического sql
                    -- если тип столбца DATE, то конвертируем полученную запись в дату
                    IF DATA_TYPE = 'DATE' THEN
                        str:=str||'to_date('''||arr(i)||''',''dd.mm.yyyy hh24:mi:ss'')'||',';
                    ELSE 
                        str:=str||''''||arr(i)||''''||',';
                    END IF;

                    -- вставляем полученную строку в таблицу
                    IF i = csvfields THEN

                            BEGIN
                                STRCOM:='INSERT INTO '||owner_name||'.'||tab_name||' values('||regexp_replace(str,',$','')||')';

                                EXECUTE IMMEDIATE STRCOM;
                                COMMIT;

                                str:=NULL;
                                arr.DELETE;
                            END;
                                              
                    END IF;
                    
               END LOOP;
               
            ELSE
                exception_msg:='Wrong number of columns '||file_name||', because csv('||csvfields||') <> tab('||colmax||').';
                EXIT;
                UTL_FILE.FCLOSE(F1);
            END IF;
       EXCEPTION WHEN No_Data_Found THEN EXIT;
       END;
                    
    END LOOP;
    -- закрываем файл
    UTL_FILE.FCLOSE(F1);
   exception_msg:=exception_msg || chr(13) || 'Data load!'; 
 ELSE
   exception_msg:='Table name OR owner name is wrong!';
 END IF;     

    EXCEPTION 
    WHEN utl_file.invalid_operation THEN exception_msg:='File '|| file_name ||' is Not Open.';
    WHEN UTL_FILE.INVALID_PATH THEN exception_msg:='Invalid file '|| file_name ||' path.';
    WHEN UTL_FILE.READ_ERROR THEN exception_msg:='Read error file '|| file_name ||'.';
    WHEN UTL_FILE.INVALID_FILEHANDLE THEN exception_msg:='File ' || file_name || ' handle was Invalid.';
    WHEN UTL_FILE.INTERNAL_ERROR THEN exception_msg:='An unspecified error in PL/SQL.';
    WHEN OTHERS THEN exception_msg:=('ERROR: '||DBMS_UTILITY.FORMAT_ERROR_STACK||DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
    
END;

 

Вызов процедуры свыполняется следующим образом:

declare
    outrep varchar2(4000);
begin
    LOADFROMFILE('DBSTAT','apex_stat_sga_16052014_14.csv','SDUSER','STAT_SGA',';',outrep);
    dbms_output.put_line(outrep);
end;

--dbms_output--
File apex_stat_sga_16052014_14.csv is Open.
Data load!

Расчет рабочих часов в указанном периоде

Я рассматривал случай рабочего дня с 08:00 по 17:00, включая обед с 12:00 по 13:00. Функция рассчитывает количество часов, которые попадают в заданный диапазон времени с учетом выходных дней (суббота и воскресенье), рамок рабочего дня и обеда.

CREATE OR REPLACE function worktime(beg_datetime in date, end_datetime in date)
return number
as
    kol_day number;
    type arr_table is table of number index by binary_integer;
    arr arr_table;
    res number default 0;
    
begin

   -- Чистим массив
   arr.delete;
   
 if beg_datetime < end_datetime then
   -- Расчет количества дней в заданном периоде дат
   kol_day := round(trunc(end_datetime) - trunc(beg_datetime));
   
   for i in 0..kol_day loop
      -- Проверка выходных дней (exclude Sunday, Saturday)
      if not to_char(beg_datetime + i, 'd') in (7, 1) then 
        -- Расчет количества рабочего времени по каждому дню
        arr(i) := case        
        -- если промежуточный день
        when not trunc(beg_datetime + i) in (trunc(beg_datetime), trunc(end_datetime)) 
            then 8
        -- если даты заданного дипазона совпадают
        when kol_day = 0 and trunc(beg_datetime + i) = trunc(end_datetime) then
           round(end_datetime - (beg_datetime + i), 3) 
        else case 
             -- если начало периода
             when trunc(beg_datetime + i) = trunc(beg_datetime) and
             to_char(beg_datetime, 'hh24') >= 8 and to_char(beg_datetime, 'hh24') < 12 
                then round((((trunc(beg_datetime) + 17/24) - beg_datetime)*24) - 1,3)
             when trunc(beg_datetime + i) = trunc(beg_datetime) and 
             to_char(beg_datetime, 'hh24') >= 13 and to_char(beg_datetime, 'hh24') < 17
                then round((((trunc(beg_datetime) + 17/24) - beg_datetime)*24),3)
             -- если конец периода
             when trunc(beg_datetime + i) = trunc(end_datetime) and
             to_char(end_datetime, 'hh24') >= 8 and to_char(end_datetime, 'hh24') < 12 
                then round((end_datetime - (trunc(end_datetime) + 8/24)) * 24,3)
             when trunc(beg_datetime + i) = trunc(end_datetime) and 
             to_char(end_datetime, 'hh24') >= 13 and to_char(end_datetime, 'hh24') < 17
                then round((end_datetime - (trunc(end_datetime ) + 8/24 + 1/24)) * 24,3)     
             -- ошибка
             else 0
             end
        end;
        
        -- Расчет суммароного рабочего времени за период
        res:= res + arr(i);
      
      end if;
                        
   end loop;

 else
 res:=-1;    
    DBMS_OUTPUT.PUT_LINE('Указан неверный диапазон дат!');
 
 end if;
   return res;
end;
/

Пример, вызова функции:

select worktime(to_date('07.04.2014 16:12:34','dd.mm.yyyy hh24:mi:ss'), to_date('20.04.2014 09:20:15','dd.mm.yyyy hh24:mi:ss')) RES from dual;

RES
---
72.791

Конвертация Unixtime в Oracle Date

Известно, что unixtime считается в секундах и начальная точка отсчета берется 01.01.1970 00:00:00. Тогда зная это можно преобразовать unixtime в oracle date по следующей формуле:

select to_char(to_date('01.01.1970 00:00:00','dd.mm.yyyy hh24:mi:ss') + 1/24/60/60 * :unixtime, 'dd.mm.yyyy hh24:mi:ss') as from_unix from dual;

Например, переменная :unixtime = 1391010620, тогда запрос вернет нам следующее:

select to_char(to_date('01.01.1970 00:00:00','dd.mm.yyyy hh24:mi:ss') + 1/24/60/60 * 1391010620, 'dd.mm.yyyy hh24:mi:ss') as from_unix from dual;

FROM_UNIX
-------------------
29.01.2014 15:50:20

Импорт из ActiveDirectory в Oracle

Подключиться из Oracle к AD возможно с помощью пакета DBMS_LDAP (доступ по Lightweight Directory Access Protocol).

Создадим таблицу для регистрации данных из AD.

CREATE TABLE USERS_AD
( CN          VARCHAR2(256 BYTE),
JOB_TITLE   VARCHAR2(256 BYTE),
DEPARTMENT  VARCHAR2(256 BYTE),
LOGIN       VARCHAR2(256 BYTE),
STATUS      VARCHAR2(256 BYTE),
DN_SHORT    VARCHAR2(128 BYTE),
DN          VARCHAR2(512 BYTE)
);

Создадим процедуру извлечения данных из AD и регистрации их в таблице USERS_AD.

CREATE OR REPLACE procedure AD_TO_DB
AS

ldap_server Varchar2 (256);
ldap_port Varchar2 (256);
ldap_dn Varchar2 (256);
ldap_user Varchar2 (256);
ldap_password Varchar2 (256);
ldap_base Varchar2 (256);
ldap_session dbms_ldap.session;
retval PLS_INTEGER;
ldap_message dbms_ldap.message;
ldap_attrs dbms_ldap.string_collection;
ldap_entry dbms_ldap.message;
ldap_attr_name VARCHAR2(256);
ldap_ber_elmt dbms_ldap.ber_element;
ldap_attr_index PLS_INTEGER;
entry_index PLS_INTEGER;
attr_index PLS_INTEGER;
i PLS_INTEGER;
ldap_vals dbms_ldap.string_collection;
ldap_log Varchar2 (256);
ldap_cn Varchar2 (256);
ldap_dep Varchar2 (256);
ldap_title Varchar2 (256);
ldap_stat Varchar2 (128);
ldap_dn_short Varchar2 (128);

begin

execute immediate 'truncate table USER_FROM_AD';
commit;

retval := -1;

--Подключение к серверу AD
ldap_server := 'ldap.terra.int'; --указать ваш dns или ip сервера AD
ldap_port := '389'; --by default TCP port 389
ldap_session := dbms_ldap.init(hostname => ldap_server,
portnum => ldap_port);
dbms_output.put_line(RPAD('LDAP session ',25,' ') || ': ' || RAWTOHEX(SUBSTR(ldap_session,1,8)) || '(returned from init)');

--Авторизация в AD
ldap_user := 'cn=droid,ou=specials,dc=terra,dc=int'; --указать пользователя домена, который имеет доступ к AD
ldap_password := 'xxxxxxx';
--запускаем простую аутентификацию к AD по имени пользователя и ввода пароля
retval := dbms_ldap.simple_bind_s(ld => ldap_session,
dn => ldap_user,
passwd => ldap_password);
dbms_output.put_line(RPAD('LDAP User ',25,' ') || ': ' || ldap_user);
dbms_output.put_line(RPAD('LDAP Pass ',25,' ') || ': ' || ldap_password);

dbms_ldap.use_exception := TRUE;

--Просмотр аттрибутов по ветке AD
ldap_base := 'ou=regions,ou=company users,dc=terra,dc=int'; --интересующая ветка AD
ldap_attrs(1) := 'cn';  --ФИО
ldap_attrs(2) := 'title'; --должность
ldap_attrs(3) := 'department';  --подразделение
ldap_attrs(4) := 'sAMAccountName';  --логин
ldap_attrs(5) := 'userAccountControl'; --статус
--filter => 'objectclass = *'
--запускаем функцию синхронного поиска в каталоге с фильтром по объектному классу - пользователи
retval := DBMS_LDAP.search_s(ld       => ldap_session,
base     => ldap_base,
scope    => DBMS_LDAP.SCOPE_SUBTREE,
filter   => 'objectclass=User',
attrs    => ldap_attrs,
attronly => 0,
res      => ldap_message);
dbms_output.put_line(RPAD('search_s Returns ',25,' ') || ': '|| TO_CHAR(retval));
dbms_output.put_line(RPAD('LDAP message ',25,' ') || ': ' || RAWTOHEX(SUBSTR(ldap_message,1,8)) || '(returned from search_s)');

--Подсчет вернувшихся значений
retval := dbms_ldap.count_entries(ld => ldap_session,
msg => ldap_message);
dbms_output.put_line(RPAD('Number of Entries ',25,' ') || ': '|| TO_CHAR(retval));

--Получение первой записи
ldap_entry := dbms_ldap.first_entry(ld => ldap_session,
msg => ldap_message);
entry_index := 0;

<< entry_loop >>
WHILE ldap_entry IS NOT NULL
LOOP

--Вывести текущую запись
ldap_dn := DBMS_LDAP.get_dn(ld => ldap_session,
ldapentry => ldap_entry);

ldap_dn_short := trim(replace(substr(replace(ldap_dn,substr(ldap_dn,-35,100),''),instr(replace(ldap_dn,substr(ldap_dn,-35,100),''),'OU=',-1)),'OU=',''));
--Получить первый атрибут
ldap_attr_name := dbms_ldap.first_attribute(ld => ldap_session,
ldapentry => ldap_entry,
ber_elem => ldap_ber_elmt);
ldap_cn := NULL;
ldap_title := NULL;
ldap_dep := NULL;
ldap_log := NULL;
ldap_stat := NULL;

attr_index := 1;
<< attributes_loop >>
WHILE ldap_attr_name IS NOT NULL
LOOP

--Получить значение, связанное с атрибутом
ldap_vals := DBMS_LDAP.get_values (ld => ldap_session,
ldapentry => ldap_entry,
attr => ldap_attr_name);

<< values_loop >>
FOR i in ldap_vals.FIRST..ldap_vals.LAST
LOOP

CASE ldap_attr_name
WHEN 'cn' THEN ldap_cn := ldap_vals(i);
WHEN 'title' THEN ldap_title := ldap_vals(i);
WHEN 'department' THEN ldap_dep := ldap_vals(i);
WHEN 'sAMAccountName' THEN ldap_log := ldap_vals(i);
WHEN 'userAccountControl' THEN ldap_stat := ldap_vals(i);
END CASE;

END LOOP values_loop;
--Следующий аттрибут
ldap_attr_name := dbms_ldap.next_attribute(ld => ldap_session,
ldapentry => ldap_entry,
ber_elem => ldap_ber_elmt);

attr_index := attr_index + 1;

END LOOP attributes_loop;

--Освобождаем ber element
IF (ldap_ber_elmt IS NOT NULL) THEN
dbms_ldap.ber_free(ber => ldap_ber_elmt, freebuf => 0);
END IF;

--Следующее значение для аттрибута
ldap_entry := dbms_ldap.next_entry(ld => ldap_session,
msg => ldap_entry);

entry_index := entry_index + 1;

--Если получены данные, то пишем их в таблицу
IF entry_index > 0 THEN
EXECUTE IMMEDIATE 'INSERT INTO USER_AD values(:1, :2, :3, :4, :5, 6:, :7)'
USING ldap_cn, ldap_title, ldap_dep, ldap_log, ldap_stat, ldap_dn_short, ldap_dn;
END IF;

END LOOP entry_loop;

COMMIT;

--Сообщение LDAP
retval := dbms_ldap.msgfree(lm => ldap_message);
DBMS_OUTPUT.PUT_LINE('AD operation successful!');

--Завершение сессии в AD
retval := DBMS_LDAP.unbind_s(ld => ldap_session);
dbms_output.put_line(RPAD('unbind_res Returns ',25,' ') || ': ' ||TO_CHAR(retval));

--Исключение ошибок
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line(' Error code : ' || TO_CHAR(SQLCODE));
dbms_output.put_line(' Error Message : ' || SQLERRM);
dbms_output.put_line(' Exception encountered .. exiting');

END AD_TO_DB;
/

Теперь можно запустить процедуру и получить пользовательские данные из AD.