Author Archive

DATE or NUMBER as a column name

One could expect that if it is possible to name a table’s column „DATE” then it is safe to do so. It appears, however, that using such a column’s name would cause the following compilation error in any procedure using an input parameter of the row type based on this table:

ORA-06552: PL/SQL: Compilation unit analysis terminated
ORA-06553: PLS-320: the declaration of the type of this expression is incomplete or malformed

Surprisingly, view on this table works correctly and you can insert data through it. It seems that only PL/SQL has this error. The same errors occurs when the column is named NUMBER. However there is no problem with names: VARCHAR, VARCHAR2, INTEGER, FLOAT, TIMESTAMP, CLOB, LOB, CHAR, LONG

Here is the test for DATE name

create table test_date ( "DATE" date );
create table test_date2 ( "DATE2" date );

-- this procedure would not compile
create procedure pr_test_date(p_date test_date%rowtype) as
begin
 null;
end;

create procedure pr_test_date2(p_date2 test_date2%rowtype) as
begin
 null;
end;

create view v_test_date as select * from test_date;
create view v_test_date2 as select * from test_date2;

insert into v_test_date("DATE") values (sysdate);

DATE lub NUMBER jako nazwa kolumny

Wydawałoby się, że skoro można nazwać kolumnę „DATE” jeśli umieści się ten wyraz w cudzysłowach to jest to bezpieczne. Okazuje się jednak, że używanie tej nazwy powoduje następujący błąd kompilacji w procedurze wykorzystującej na wejściu typ wierszowy na tej tabeli:

ORA-06552: PL/SQL: Compilation unit analysis terminated
ORA-06553: PLS-320: the declaration of the type of this expression is incomplete or malformed

Dzieje się tak, mimo że widok na tej tabeli działa poprawnie i można przez niego wrzucać dane. Podobny błąd występuje, gdy kolumna nazywa się NUMBER, ale dla VARCHAR, VARCHAR2, INTEGER, FLOAT, TIMESTAMP, CLOB, LOB, CHAR, LONG precedura działa poprawnie.

Poniżej test dla typu DATE

create table test_date ( "DATE" date );
create table test_date2 ( "DATE2" date );

-- ta procedura się nie kompiluje
create procedure pr_test_date(p_date test_date%rowtype) as
begin
 null;
end;

create procedure pr_test_date2(p_date2 test_date2%rowtype) as
begin
 null;
end;

create view v_test_date as select * from test_date;
create view v_test_date2 as select * from test_date2;

insert into v_test_date("DATE") values (sysdate);

passing arguments from Java to Oracle stored procedure

This is an example about how to pass string, string array or string pairs array from Javy to Oracle

You should create the following types in your database:

create or replace type VARCHAR2_PAIR as object( k varchar2(200), v varchar2(200));
create or replace type VARCHAR2_PAIRS_TABLE as table of VARCHAR2_PAIR;
create or replace type VARCHAR2_TABLE as table of VARCHAR2(200);

.

Then you should create stored procedure of the following signature

CREATE OR REPLACE PROCEDURE arrays_from_java(p_scalar varchar2
, p_array IN VARCHAR2_TABLE
, p_pairs_array in VARCHAR2_PAIRS_TABLE);

.

And in the end call it from Java

public static void callArrayProcedure() throws Exception{
    System.out.println("Preparing data");
    String[] list1=new String[2];
    list1[0] = "first string";
    list1[1] = "second string";

    String[][] list2=new String[2][];
    {
        String[] pair1 = new String[2];
        pair1[0] = "key1";
        pair1[1] = "value1";
        list2[0] = pair1;
    }
    {
        String[] pair2 = new String[2];
        pair2[0] = "key2";
        pair2[1] = "value2";
        list2[1] = pair2;
    }

    System.out.println("Preparing connection");
    SingleConnectionDataSource scds = new SingleConnectionDataSource();
    scds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
    scds.setUrl("jdbc:oracle:thin:@host:1521:service_name");
    scds.setUsername(...);
    scds.setPassword(...);
    Connection conn = scds.getConnection();

    System.out.println("Preparing array of strings");
    ArrayDescriptor descriptor1 = ArrayDescriptor.createDescriptor("VARCHAR2_TABLE", conn );
    ARRAY arrayToPass1 = new ARRAY( descriptor1, conn, list1 );

    System.out.println("Preparing array of string pairs");
    ArrayDescriptor descriptor2 = ArrayDescriptor.createDescriptor("VARCHAR2_PAIRS_TABLE", conn );
    ARRAY arrayToPass2 = new ARRAY( descriptor2, conn, list2 );

    System.out.println("Calling procedure");
    OraclePreparedStatement ps = (OraclePreparedStatement) conn.prepareStatement( "{ call arrays_from_java( ?, ?, ? ) }" );
    ps.setString( 1, "string");
    ps.setARRAY( 2, arrayToPass1 );
    ps.setARRAY( 3, arrayToPass2 );
    ps.execute();
    ps.close();
}

.

You should also add orai18n.jar to Java classpath, otherwise strings may become null after passing them to stored procedure if the basic character set (NLS_CHARACTERSET) is not UTF


Przekazywanie tablicy rekordów z Javy do procedury składowanej w Oracle’u

Poniżej znajduje się opis jak przekazywać string, tablicę stringów oraz tablicę par stringów z Javy do Oracle’a

Należy utworzyć typy w bazie danych:

create or replace type VARCHAR2_PAIR as object( k varchar2(200), v varchar2(200));
create or replace type VARCHAR2_PAIRS_TABLE as table of VARCHAR2_PAIR;
create or replace type VARCHAR2_TABLE as table of VARCHAR2(200);

.

Potem utworzyć procedurę składowaną

CREATE OR REPLACE PROCEDURE arrays_from_java(p_scalar varchar2
, p_array IN VARCHAR2_TABLE
, p_pairs_array in VARCHAR2_PAIRS_TABLE);

.

A na końcu zawołać ją z Javy:

public static void callArrayProcedure() throws Exception{
    System.out.println("Preparing data");
    String[] list1=new String[2];
    list1[0] = "first string";
    list1[1] = "second string";

    String[][] list2=new String[2][];
    {
        String[] pair1 = new String[2];
        pair1[0] = "key1";
        pair1[1] = "value1";
        list2[0] = pair1;
    }
    {
        String[] pair2 = new String[2];
        pair2[0] = "key2";
        pair2[1] = "value2";
        list2[1] = pair2;
    }

    System.out.println("Preparing connection");
    SingleConnectionDataSource scds = new SingleConnectionDataSource();
    scds.setDriverClassName("oracle.jdbc.driver.OracleDriver");
    scds.setUrl("jdbc:oracle:thin:@host:1521:service_name");
    scds.setUsername(...);
    scds.setPassword(...);
    Connection conn = scds.getConnection();

    System.out.println("Preparing array of strings");
    ArrayDescriptor descriptor1 = ArrayDescriptor.createDescriptor("VARCHAR2_TABLE", conn );
    ARRAY arrayToPass1 = new ARRAY( descriptor1, conn, list1 );

    System.out.println("Preparing array of string pairs");
    ArrayDescriptor descriptor2 = ArrayDescriptor.createDescriptor("VARCHAR2_PAIRS_TABLE", conn );
    ARRAY arrayToPass2 = new ARRAY( descriptor2, conn, list2 );

    System.out.println("Calling procedure");
    OraclePreparedStatement ps = (OraclePreparedStatement) conn.prepareStatement( "{ call arrays_from_java( ?, ?, ? ) }" );
    ps.setString( 1, "string");
    ps.setARRAY( 2, arrayToPass1 );
    ps.setARRAY( 3, arrayToPass2 );
    ps.execute();
    ps.close();
}

.

Należy też dodać do classpath orai18n.jar, gdyż w przeciwnym wypadku jeśli baza nie będzie miała formatu UTF8 może występować błąd, że stringi się nullują podczas przekazywania do procedury


import z bazy z błędnym kodowaniem

Ostatnio musiałem importować dane z bazy danych z kodowaniem w której zachodnioeuropejskiego w której były przechowywane polskie znaki. Było z tym trochę problemów, więc opiszę rozwiązanie

Zagadnienie

Baza A – źródłowa, u klienta. Kodowanie zachodnioeuropejskie WE8DEC. Przechowywane są w niej jednak polskie znaki w kodowaniu EE8ISO8859P2 czyli ISO-8859-2. Takie kodowanie stosuje klient bazy. Oracle pozwala na takie kombinacje. Miałem kiedyś do czynienia z bazą danych w kodowaniu US7ASCII, która też przechowywała polskie znaki, mimo że to kodowanie zawiera tylko na znaki do kodu 127 a więc bez narodowych. Można zapisywać lub odczytywać dane z poziomu klienta tak aby konwersje nie miały miejsca – dzięki temu obce kodowanie nie przeszkadza. Problem pojawił się przy dostępie przez DB linki, ale o tym za chwilę.

Baza B – docelowa, u nas. Optymalne kodowanie AL32UTF8 pozwalające przechowywać wszystkie znaki łącznie ze wschodnioazjatyckimi.
(continue reading…)


Subtle feature of CONNECT BY

Hierarchical queries (CONNECT BY … START WITH …) are used when retrieving data from tree-like structures. You use them if for example you want to retrieve all people who are directly or indirectly below a certain person in a company hierarchy. However, if you use joins and add some other conditions in the WHERE clause then the effect of the query is not obvious.
(continue reading…)


Accessing Microsoft SQL Server from Oracle

Once we had a need of accessing a table on an Microsoft SQL Server database from an Oracle database. Thus we had to install Oracle Transparent Server on the computer hosting SQL Server. Oracle Transparent Services is a kind of adapter for non-Oracle servers and allows access to it through database link. During the instalation of Oracle Transparent Server you have to choose modules dedicated to particular databases. The modules can translate calls to some functions and allows to call a T-SQL stored procedure from Oracle.
(continue reading…)


Solving package dependency problem using synonyms

Problem

I take part in supporting an ODS system. One of the biggest problem during deployment of revisions is the dependency between packages. Suppose a package A has to be changed and that it is referenced be two packages: B1 and B2 which are used in some database sessions. If the the A package is altered and it contained some global variables then the next execution of the packages B1 or B2 in those sessions triggers „ORA-04061: existing state of package has been invalidated” error. This is because global variables preserve their values during database sessions. Alteration of the package code may change the variables, delete some of them, add others. Therefore, they have to be initialized again. Reseting the global variables during the session could lead to inconsistency. In order to prevent this. Oracle throws the ORA-04061 exception. This mechanism is described in details on the page http://oraclequirks.blogspot.com/2007/03/ora-04061-existing-state-of-package-has.html
(continue reading…)


Rozwiązanie problemów z zależnościami pakietu poprzez synonimy

Problem

Biorę udział w utrzymywaniu pewnego systemu typu ODS. Jednym z poważniejszych problemów podczas wgrywania poprawek jest sprawa zależności między pakietami. Przypuśćmy, że trzeba zmienić pakiet A, od którego zależą pakiety B1 i B2, które są wykorzystywane w ramach jakiejś sesji. Zmiana w A powoduje, że najbliższe uruchomienia pakietów B1 lub B2 bez tworzenia nowej sesji zakończą się błędem „ORA-04061: existing state of package has been invalidated”, jeśli tylko w A są jakiekolwiek zmienne globalne. To zachowanie Oracle’a wynika z faktu, że zmienne globalnych pakietów trwają przez cały czas życia sesji. Przy zmianie kodu pakietu mogły się one zmienić, dlatego muszą zostać zainicjowane na nowo. Ponieważ zresetowanie zmiennych w czasie trwania sesji mogłoby prowadzić do niespójności – Oracle rzuca opisany powyżej wyjątek. Ten mechanizm jest dokładniej opisany na stronie http://oraclequirks.blogspot.com/2007/03/ora-04061-existing-state-of-package-has.html

Zmiana pakietu – zarówno specyfikacji jak i ciała pakietu – nie jest możliwa jeśli coś z niego korzysta – w takim wypadku komenda zastąpienia pakietu przez nową wersję ( CREATE OR REPLACE PACKAGE [BODY]) czeka aż pakiet przestanie być używany. Ale wystarczy, aby jakiś nowy proces zaczynał korzystać z pakietu zanim stary przestanie to robić i następuje zagłodzenie. Nie można wgrać nowej wersji pakietu. Takie zjawiska bardzo utrudniają wdrażanie, trzeba zabijać procesy korzystające ze zmienianego pakietu. Wypracowałem nawet skrypt, który zabija wszystkie sesje blokujące jakąś sesję uruchomioną z mojego komputera i mojego użytkownika systemu operacyjnego:

DECLARE
  CURSOR c_sql_hist(pp_machine VARCHAR2, pp_osuser VARCHAR2) IS
    SELECT ash.blocking_session, ash.blocking_session_status, ash.blocking_session_serial#, s.program, s.module, s.action
      FROM v$active_session_history ash
      JOIN v$session s ON ash.blocking_session = s.sid
                      AND ash.blocking_session_serial# = s.serial#
     WHERE ash.session_id in ( select sid from v$session where machine = pp_machine
     and osuser = pp_osuser )
       AND ash.sample_time > SYSDATE - 2 / 24 / 3600
     ORDER BY sample_time DESC;
  v_r c_sql_hist%ROWTYPE;

  v_my_machine v$session.MACHINE%TYPE;
  v_my_osuser v$session.osuser%TYPE;
BEGIN
  select machine, osuser into v_my_machine, v_my_osuser from v$session
  where sid = ( Select Sid from v$mystat where rownum = 1);

  for i in 1..10000 loop  -- just to prevent infinite execution
    dbms_output.put_line('step: ' || i );
    OPEN c_sql_hist(v_my_machine, v_my_osuser);
    FETCH c_sql_hist
      INTO v_r;
    IF NOT c_sql_hist%NOTFOUND THEN
      dbms_output.put_line(v_r.blocking_session || ' ' || v_r.blocking_session_status || ' ' || v_r.blocking_session_serial# || ' ' || v_r.program || ' ' ||
                           v_r.module || ' ' || v_r.action);
      execute immediate ' ALTER SYSTEM KILL SESSION ''' || v_r.blocking_session || ',' || v_r.blocking_session_serial# || '''';
    else
      exit;
    END IF;
    CLOSE c_sql_hist;
    dbms_lock.sleep(0.5); -- sleep is used to get new blocking sessions
  end loop;
END;

Opisane powyżej problemy z zależnościami doprowadziły do sytuacji, w których różne pakiety narzędziowe, które powinny być zgromadzone w jednym centralnym schemacie zostały skopiowane do wszystkich schematów. Dzięki temu można je zmieniać bez konieczności zabijania wszystkich procesów działających na bazie, ale jest to rozwiązanie fatalne z punktu widzenia utrzymania kodu.

Rozwiązanie

Stosowanym przez nas rozwiązaniem tego problemu jest, aby odwołania do pakietów odbywały się poprzez synonimy. Jeśli jest potrzeba zmienić pakiet – tworzymy jego nową wersję a po przetestowaniu przepina się synonim, aby na nią wskazywał. Najbliższe uruchomienie tego synonimu w ramach sesji powoduje, że uruchamiany jest nowy pakiet. Jego zmienne są inicjalizowane na nowo, ale nie jest to problem, ponieważ pełnią funkcje cache’a a nie służą do przechowywania stanu.

Występuje dodatkowe ograniczenie, że nie mogą chodzić równocześnie dwie wersje tej samej procedury lub pakietu, których synonimy wskazują na odmienne obiekty. Dlatego jeśli uruchomi się B1, przepnie synonim na A_v2, po czym ponownie uruchomi B1 (pośrednio lub bezpośrednio) to to drugie uruchomienie B1 nie rozpocznie się dopóki pierwsze – na starym synonimie – się nie skończy. Nie jest to jednak praktyczny problem.

Opisane obejście pozwala na sprawniejszy rozwój i wdrażanie kodu bazodanowego. Konieczna jest tylko świadomość działania mechanizmu i powyższych ograniczeń. Należało również wypracować praktykę nazewnictwa (przykładowo dodawanie na końcu nazwy TO_CHAR(sysdate, ‘$MM_DD’) czyli znaku dolara oraz miesiącu i dnia wdrożenia wersji). Konieczne jest również nadanie innym schematom tych samych praw do nowej wersji pakietu, które mieli do starej wersji.


Dostęp do MS Sql Servera z poziomu Oracle’a

Ostatnio pojawiła się potrzeba odczytywania z bazy danych w Oracle’u pewnej tabeli z bazy na Microsoft SQL Serverze. Rozwiązaniem było zainstalowanie na maszynie z SQL Serverem tzw. Oracle Transparent Servera czyli programu, który stanowi swego rodzaju opakowanie na nie-Oracle’owy serwer umożliwiający dostęp do niego w bazie Oracle’a poprzez db link. Poszczególne moduły Oracle Transparent Servera wybierane podczas instalacji są dedykowane do konkretnych baz danych. Potrafią one nawet przetłumaczyć odwołania do części funkcji w zapytaniach i umożliwiają np. wywoływanie z poziomu Oracle’a procedury składowanej w T-SQLu.

Oracle Transparent Server można ściągnąć zhttp://www.oracle.com/technology/software/products/database/oracle10g/htdocs/10201winsoft.html 

Opis działania znajduje się np. tutaj: http://oracle-apps-dba.blogspot.com/2008/04/oracle-transparent-gateways-general.html

Instrukcje instalacji są na następnej stronie: http://oracle-apps-dba.blogspot.com/2008/04/oracle-transparent-gateway-for-ms-sql_16.html

Instrukcje można znaleźć również na: http://forums.oracle.com/forums/thread.jspa?messageID=2720049&#2720049

Wystąpiła pewna różnice w stosunku do opisów: większość plików konfiguracyjnych znajdowała się w pliku network\admin a nie tg4msql\admin

Dostęp do danych jest powolny – najprostsze zapytanie trwa 4 sekundy. Poprawa wydajności rozwiązania jest jednak osobnym tematem.


  • About Us

    We create information and telecommunication technologies for large and medium-sized enterprises. They are based on recognized, primarily open standards and technologies, which are to guarantee our customers the highest quality and stability of information systems development. At the same time building our competencies, we are trying to shape such standards and support the development of open technologies.
    Copyright © 2002-2011 TouK sp. z o.o. s.k.a.
    iDream theme by Templates Next | Powered by WordPress