Showing posts with label Oracle 9i. Show all posts
Showing posts with label Oracle 9i. Show all posts

Thursday, March 19, 2009

ORA-00904 when clicking on edit in SQLDeveloper 1.5.4

Always check out the original article at http://www.oraclequirks.com for latest comments, fixes and updates.

If you are wondering why you are getting the following pop-up screen when attempting to edit a procedure (or a trigger, a type or anything else that can be edited in the editor panel, but not views for instance) after upgrading to the latest version of SQL Developer (1.5.4)...


ORA-00904: "ATTRIBUTE": invalid identifier
vendor code 904


... you may ask yourself if you are trying to perform this operation on a Oracle 9i database.

Please note that the annoying pop-up message doesn't prevent you from editing the source and apparently you can still compile it successfully, although every time you hit on the compile icon you'll get the exception again.
It looks like a problem with some underlying view or table that is queried by SQL Developer and is lacking a column in oracle 9i, but this is just a speculation of mine.

Initially i had a feeling that someone had told me that as of version 1.5.x Oracle 9i was no longer supported, but when i read the release notes i read that Oracle 8.1.7 was no longer supported, so i failed to find a specific statement about Oracle 9i.
I must conclude that it is still supported or i did not search hard enough in the accompanying documentation...

If i click on EDIT using SQL Developer 1.5.1 (the previous version installed on my PC) i don't get any errors of this kind.

See message translations for ORA-00904 or read more articles about SQL Developer.

Thursday, January 03, 2008

ORA-06531: Reference to uninitialized collection

A recent comment of a reader about an obscure PL/SQL compiler error suggested me to begin writing a few postings about errors that you may come across when working with collections, so this is the first of a series, that i don't know yet how short or long will be, not counting the errors already described in previous articles.

Let's have a look at one of the most common ones as it is reported by SQLDeveloper:
ORA-06531: Reference to uninitialized collection
ORA-06512: at line 10
06531. 00000 - "Reference to uninitialized collection"
*Cause: An element or member function of a nested table or varray
was referenced (where an initialized collection is needed)
without the collection having been initialized.
*Action: Initialize the collection with an appropriate constructor
or whole-object assignment.

What does this mean?
It's easy to explain, let's take the following PL/SQL block:
DECLARE
TYPE array_type IS VARRAY(10) OF VARCHAR2(200);
my_array array_type;
BEGIN
dbms_output.enable;
dbms_output.put_line(my_array.COUNT);
END;

ORA-06531: Reference to uninitialized collection
ORA-06512: at line 6
You cannot use the COUNT method without first initializing the collection my_array.
Likewise, you cannot use the LIMIT method either, even if LIMIT refers to the upper bound that was specified in the declaration and theoretically has little to do with the actual varray content (see below for an example).
If you need to store the varray size in a variable for easier referencing for instance or you don't want to clutter the source with such literal values, you'll need to initialize the array first, as explained later on.

my_array has been declared of type array_type, but it has not been initialized and in this situation the collection is said to be atomically NULL. Atomically null means that there are no items whatsoever in the collection that is why you cannot count them.

In order to initialize a collection you must use the type constructor, that strange beast that is named after the collection type (array_type in my example):
DECLARE
TYPE array_type IS VARRAY(10) OF VARCHAR2(200);
my_array array_type;
BEGIN
my_array := array_type();
dbms_output.enable;
dbms_output.put_line(my_array.COUNT);
END;
Alternatively and according to a better programming practice, you can initialize the collection at declaration time:
DECLARE
TYPE array_type IS VARRAY(10) OF VARCHAR2(200);
my_array array_type := array_type();
BEGIN
dbms_output.enable;
dbms_output.put_line(my_array.COUNT);
END;
both programs will return the value 0 (zero) in the dbms_output buffer.

So now we have a VARRAY with zero elements, but we declared it to hold up to 10 items.
Let's have a look at how to initialize this collection with a given number of elements.
DECLARE
TYPE array_type IS VARRAY(10) OF VARCHAR2(200);
my_array array_type := array_type('a','b');
BEGIN
dbms_output.enable;
dbms_output.put_line(my_array.COUNT);
END;
In dbms_output you'll find now the number 2 because we initialized the varray with two elements: a and b.

Imagine however that you have a large number of elements, say 32000, clearly you cannot type all of them in the constructor, so, how do you proceed?

If you are tempted to initialize the last element of the collection, see the next posting, so that is not an option.

How do you fully initialize a 32000 elements varray?

Before adding anything else, let me just suggest to ask yourself whether this is really necessary.
If the answer is yes, then read on, otherwise try to implement some other algorithm that doesn't consume db's resources so savagely...
Are you absolutely sure that this tiny program will not end up being used by dozen of concurrent users?

All right, so here comes into play the EXTEND collection method that allows us to initialize the varray by the desired number of null elements:
DECLARE
TYPE array_type IS VARRAY(32000) OF VARCHAR2(200);
my_array array_type := array_type();
BEGIN
my_array.EXTEND(32000);
dbms_output.enable;
dbms_output.put_line(my_array.COUNT);
END;
Last note: interestingly enough, the official documentation says that this form of EXTEND cannot be used when you impose a NOT NULL constraint on the array (or table) type, but, at least on Oracle XE, this is not true:
DECLARE
TYPE array_type IS VARRAY(32000) OF VARCHAR2(200) NOT NULL;
my_array array_type := array_type();
BEGIN
dbms_output.enable;
my_array.EXTEND(31000);
dbms_output.put_line(my_array.COUNT);
dbms_output.put_line(nvl(my_array(31000),'31000th is null!'));
END;

Indeed if you try to initialize the array using a non empty constructor containing nulls, Oracle will complain at parse time:
DECLARE
TYPE array_type IS VARRAY(32000) OF VARCHAR2(200) NOT NULL;
my_array array_type := array_type(null,null);
BEGIN
dbms_output.enable;
my_array.EXTEND(31000);
dbms_output.put_line(my_array.COUNT);
dbms_output.put_line(nvl(my_array(31000),'31000th is null'));
END;

Error report:
ORA-06550: line 3, column 42:
PLS-00567: cannot pass NULL to a NOT NULL constrained formal parameter
So, either i got it wrong or this is a bug...

Last but not least, let's peek at the most sensible and probably useful way of initializing a collection that is by using bulk SQL:
DECLARE
TYPE array_type IS VARRAY(32000) OF VARCHAR2(200);
my_array array_type := array_type();
upper_bound pls_integer := my_array.LIMIT;
BEGIN
dbms_output.enable;

select 'item_' || n
bulk collect into my_array
from (
select level n
from dual
connect by level <= upper_bound);

dbms_output.put_line(my_array.COUNT);
dbms_output.put_line(nvl(my_array(31000),'31000th is null'));
END;
Please note that i had to explicitly initialize the array because i used LIMIT for retrieving the array upper bound as i don't wanted to hardcode the literal 32000 inside the query, but if you don't use this kind of approach, you can omit the array initialization, it will be performed automatically when BULK COLLECT is performed.

------------------------------------------------
ORA-06531: Riferimento a collection non inizializzata
ORA-06531: Referencia a una recopilación no inicializada
ORA-06531: Referència a recollida no inicialitzada
ORA-06531: Référence à un ensemble non initialisé
ORA-06531: Nicht initialisierte Zusammenstellung referenziert
ORA-06531: Αναφορά σε μη αρχικοποιημένη συλλογή
ORA-06531: Reference til ikke-initialiseret samling
ORA-06531: Referens till ej initierad insamling
ORA-06531: Referanse til uinitialisert samling
ORA-06531: Viittaus alustamattomaan kokoelma
ORA-06531: Inicializálatlan gyűjtőre való hivatkozás
ORA-06531: Referinţă la o colecţie neiniţializată
ORA-06531: Verwijzing naar niet-geïnitialiseerde verzameling.
ORA-06531: Referência para coleta não-inicializada
ORA-06531: Referência a uma recolha não inicializada
ORA-06531: Ссылка на неинициализированный набор
ORA-06531: Odkaz na neinicializovanou skupinu
ORA-06531: Odkaz na neiniciovanú kolekciu
ORA-06531: Odwołanie do nie zainicjowanej kolekcji
ORA-06531: Başlatılmamış koleksiyona başvuru
ORA-06531: Reference to uninitialized collection

See message translations for ORA-06531 and search additional resources

Tuesday, July 10, 2007

ORA-02266: unique/primary keys in table referenced by enabled foreign keys

This error is easily explained with the help of a picture, courtesy of Oracle Application Express Model View:


Suppose you have a LOCATIONS table with a column LOCATION_ID defined as primary key (or with a unique constraint on it), then another table called STORES, with a column called LOCATION_ID and a foreign key constraint called FK_LOCATION_ID that is referencing the LOCATION_ID column of table LOCATIONS (graphically rendered in the picture by the shape -[ which represents a one-to-many relationship).

No matter if this constraint is defined as ON DELETE CASCADE or ON DELETE SET NULL or the default (NO ACTION), if you try to do the following:

TRUNCATE TABLE LOCATIONS
/
You will get:
ORA-02266: unique/primary keys in table referenced by enabled foreign keys

Please note that the error is raised regardless of the presence of data in the child tables.

It is perfectly normal that Oracle prevents you from doing this, however if all child tables are empty, then you can safely disable the referential constraints and re-issue the TRUNCATE.
But don't forget to re-enable the constraints afterwards.

If you need to purge a whole set of these intertwined tables, such as when you need to reset a test scenario before reloading a test data set, then it can be useful to have a script that temporarily disables all the constraints on the child tables and iteratively truncates all of them.

Of course you need to make sure that you (or someone else...) do not run under any circumstances such script in a production environment.

Note that you can use SQL queries to spawn the DDL statements required to perform this tasks and spool the results as a SQL script that you call right away.
First i create a list of currently enabled constraints, because i don't want to enable constraints that were disabled for some other reason, then i create the disabling statements and finally the truncate statements. Once all the auto-generated scripts are ready, i can run them in the sequence: disable-truncate-enable.
create procedure force_truncate(
csv_of_tables IN VARCHAR2,
display_only IN BOOLEAN DEFAULT TRUE
)
is
--
list_of_tables DBMS_UTILITY.UNCL_ARRAY;

TYPE str_tab IS TABLE OF VARCHAR2(255);
tab_of_DDLs str_tab;

TYPE assoc_tab IS TABLE OF VARCHAR2(5) INDEX BY VARCHAR2(61);
trunc_list assoc_tab;

tmp INTEGER;
n INTEGER;
stop_on_err BOOLEAN DEFAULT FALSE;
show_warning BOOLEAN DEFAULT FALSE;

cursor cur_child_tables(p_tab_name in user_tables.table_name%type) is
select distinct owner||'.'||table_name
as child_name
from user_constraints
where (r_constraint_name, r_owner)
in (select constraint_name, owner
from user_constraints
where constraint_type in ('P','U')
and table_name = p_tab_name
and status = 'ENABLED');

cursor cur_tables(p_tab_name in user_tables.table_name%type) is
select 1 as ok
from user_tables
where table_name = p_tab_name;

cursor cur_grantees(p_tab_name in user_tab_privs_made.table_name%type) is
select grantee
from user_tab_privs_made
where privilege = 'REFERENCES'
and table_name = p_tab_name;

cursor cur_constraints(p_tab_name in user_tables.table_name%type) is
select owner, table_name, constraint_name
from user_constraints
where (r_constraint_name, r_owner)
in (select constraint_name, owner
from user_constraints
where constraint_type in ('P','U')
and table_name = p_tab_name
and status = 'ENABLED');

-- private subroutine
procedure exec_and_log(
DDL IN VARCHAR2,
display_only IN BOOLEAN
)
is
begin
dbms_output.put_line(DDL);
dbms_output.put_line('/');
if not display_only then
begin
execute immediate DDL;
dbms_output.put_line('--done');
exception
when others then
dbms_output.put_line(sqlerrm);
end;
end if;
end;
-- main procedure
begin
if display_only then
dbms_output.put_line('running in display-only mode, DDLs will not be executed!');
dbms_output.new_line;
else
dbms_output.put_line('running DDLs, check for execution errors');
dbms_output.new_line;
end if;

dbms_output.new_line;

dbms_utility.comma_to_table(upper(csv_of_tables), n, list_of_tables);
tab_of_DDLs := str_tab();

for each_entry in 1..n
loop
trunc_list(user||'.'||trim(list_of_tables(each_entry))) := '...ok';
end loop;

-- check if child tables may cause some trouble
for each_entry in 1..n
loop
dbms_output.put_line('Checking...'||trim(list_of_tables(each_entry)));


begin
select 1
into tmp
from user_tables
where table_name = trim(list_of_tables(each_entry));
exception
when no_data_found then
stop_on_err := TRUE;
dbms_output.put_line(trim(list_of_tables(each_entry))
||' is not a user table');
end;

for each_grantee in cur_grantees(trim(list_of_tables(each_entry)))
loop
dbms_output.put_line('WARNING: user ' || each_grantee.grantee
|| ' has been granted REFERENCES on this table');
show_warning := TRUE;
end loop;

if show_warning then
dbms_output.new_line;
dbms_output.put_line('Truncates on table '
||trim(list_of_tables(each_entry))|| ' may fail');
dbms_output.new_line;
show_warning := FALSE;
end if;

for each_child in cur_child_tables(trim(list_of_tables(each_entry)))
loop
begin
dbms_output.put(trim(list_of_tables(each_entry))
||' has child table '||each_child.child_name);
-- if next line fails, a not listed child table has been found
dbms_output.put_line(trunc_list(each_child.child_name));
exception
when no_data_found then
stop_on_err := TRUE;
dbms_output.put_line('...not found in truncate list');
when others then raise;
end;
end loop;
end loop;

if stop_on_err then
dbms_output.put_line('aborting truncates...');
return;
end if;
dbms_output.new_line;

for each_entry in 1..n loop
-- prepare constraint enable for later use
for each_constraint in cur_constraints(trim(list_of_tables(each_entry)))
loop
tab_of_DDLs.EXTEND;
tab_of_DDLs(tab_of_DDLs.LAST) :=
'ALTER TABLE '
||each_constraint.owner||'.'||each_constraint.table_name
||' ENABLE CONSTRAINT '||each_constraint.constraint_name;
-- perform disable of constraints
exec_and_log('ALTER TABLE '
||each_constraint.owner||'.'||each_constraint.table_name
||' DISABLE CONSTRAINT '||each_constraint.constraint_name,
display_only);
end loop;
-- perform truncates
exec_and_log('TRUNCATE TABLE '||trim(list_of_tables(each_entry))
||' DROP STORAGE', display_only);

end loop;
-- re-enable constraints
if tab_of_DDLs.exists(1) then
for each_ddl in tab_of_DDLs.FIRST..tab_of_DDLs.LAST loop
exec_and_log(tab_of_DDLs(each_ddl), display_only);
end loop;
end if;
end;
Example:

force_truncate('LOCATIONS,STORES');

dbms_output:

Checking...LOCATIONS
WARNING: user YOCOYATX has been granted REFERENCES on this table
WARNING: user YOCOYA has been granted REFERENCES on this table

Truncates on table LOCATIONS may fail

LOCATIONS has child table TEST.STORES...ok
Checking...STORES

ALTER TABLE TEST.STORES DISABLE CONSTRAINT FK_LOCATION
/
TRUNCATE TABLE LOCATIONS DROP STORAGE
/
TRUNCATE TABLE STORES DROP STORAGE
/
ALTER TABLE TEST.STORES ENABLE CONSTRAINT FK_LOCATION
/
REMARKS:
  1. The script above is provided with no warranty whatsoever, you can use it at your own risk.
  2. By default the script works in display only mode. In order to perform the DDLs, pass FALSE to parameter display_only.
  3. It is recommendable to run the procedure in display_only mode first and then to inspect the output.
  4. You have the option of copying and pasting the output statements in a sql script and run them manually if you prefer.
  5. The script checks if the supplied list of tables includes all child tables referenced by foreign keys and it quits if the condition is not met. This security check prevents a user from truncating a partial set of tables that would otherwise leave some constraints in an invalid state.
  6. The script is meant for use with tables located within the user schema. Constraints created by other users holding REFERENCES privileges cannot be seen and they will cause ORA-02266 errors. The programs shows a warning when it finds that such privileges have been granted to other users on the relevant tables.
  7. EXECUTE privilege on package DBMS_UTILITY is normally granted to PUBLIC at database installation time, so the procedure should compile without problems.


See message translations for ORA-02266 and search additional resources.



ORA-02266: La tabella referenziata da chiavi esterne abilitate dispone di chiavi uniche/primarie
ORA-02266: claves únicas/primarias en la tabla referidas por claves ajenas activadas
ORA-02266: les claus úniques/primàries de la taula estan referenciades per claus foranes activades
ORA-02266: Les clés primaires/uniques de la table référencées par des clés étrangères
ORA-02266: Eindeutige/primäre Schlüssel in Tabelle, auf die von aktivierten Fremdschlüsseln referenziert wird.
ORA-02266: μοναδικά/πρωτεύοντα κλειδιά σε πίνακα αναφέρονται από ενεργοποιημένα ξένα κλειδιά
ORA-02266: tabel har unikke/primære nøgler, der refereres til af aktiverede fremmede nøgler
ORA-02266: unika/primära nycklar i tabellen refereras via aktiverade främmande nycklar
ORA-02266: entydige/primære nøkler i tabellen refereres av aktiverte fremmednøkler
ORA-02266: käyttöön otetut viiteavaimet viittaavat taulun yksilöiviin tai perusavaimiin
ORA-02266: a táblának van olyan egyedi/elsődleges kulcsa, melyre engedélyezett külső kulcs hivatkozik
ORA-02266: cheile unice/primare din tabelă sunt referite de cheile externe activate
ORA-02266: Geactiveerde verwijzende sleutels verwijzen naar unieke/primaire sleutels in tabel.
ORA-02266: chaves exclusiva/primária na tabela referenciadas por chaves externas ativadas
ORA-02266: chaves exclusivas/primárias referenciadas por chaves externas activadas
ORA-02266: на уник./перв. ключи в таблице ссылаются вторичные ключи
ORA-02266: v tabulce existují unikátní/primární klíče odkazované odblokovanými cizími klíči
ORA-02266: jednoznačné/primárne kľúče v tabuľke sú odkazované aktivovanými cudzími kľúčmi
ORA-02266: tabela ma unikatowe/główne klucze do których odwołują się obce klucze
ORA-02266: tabloda etkin yabancı anahtarlar tarafından başvurulan benzersiz/birincil anahtarlar var

Wednesday, June 13, 2007

ORA-02393: exceeded call limit on CPU usage

If you get the following message:
ORA-02393: exceeded call limit on CPU usage
it means that there is a profile attached to the user definition with an enforced limit on the CPU_PER_CALL parameter.

Such restrictions work only if system parameter RESOURCE_LIMIT is set to TRUE, which can also be set dynamically with

ALTER SYSTEM SET RESOURCE_LIMIT = TRUE SCOPE=MEMORY;

but are not enforced for user sessions there were already connected prior to executing the ALTER command.

In other words, if you want to enforce these limits, you'll need first to disconnect all affected user sessions or bounce the database supposing that you made the change permanent by indicating SCOPE=SPFILE or SCOPE=BOTH.

Note also that profile changes incur in the same problem, so if the DBA adjusts your CPU_PER_CALL limit, the change will not affect currently opened sessions.

Unfortunately there is no built-in view that allows a normal user to query his/her profile limits.
Only the DBA or a user with the necessary privileges can query view DBA_PROFILES and the usual alternate views beginning with the prefixes ALL and USER do not exist in this case.

See message translations for ORA-02393 and search additional resources.




ORA-02393: superato il limite di chiamate sull'uso della CPU
ORA-02393: ha excedido el límite de llamadas para uso de CPU
ORA-02393: s'ha excedit el límit de crides en l'ús de la CPU
ORA-02393: limite d'appel sur utilisation CPU dépassée
ORA-02393: Aufrufgrenze (Call limit) für CPU-Nutzung überschritten
ORA-02393: ξεπεράσθηκε το όριο κλήσεων για χρήση ΚΜΕ
ORA-02393: overskrider opkaldsgrænsen for CPU-forbrug
ORA-02393: högsta antalet anrop för CPU-bruk har överskridits
ORA-02393: overskred kallgrensen for prosessorbruk
ORA-02393: kutsujen enimmäismäärä keskusyksikössä ylitetty
ORA-02393: CPU hívás szám korlát túllépve
ORA-02393: s-a depăşit limita de apel la folosirea CPU
ORA-02393: Aanroeplimiet voor CPU-gebruik is overschreden.
ORA-02393: limite de chamada excedido no uso de CPU
ORA-02393: excedido limite de chamada na utilização de CPU
ORA-02393: превышен предел вызовов на использование CPU
ORA-02393: překročen limit volání na využití CPU
ORA-02393: prekročený limit spojenia na čas CPU
ORA-02393: przekroczono ograniczenie użycia CPU przez wywołanie
ORA-02393: aramanın CPU kullanımı sınırı aşıldı

Thursday, June 07, 2007

ORA-01114 and ORA-27063

On an Solaris platform you may see an error message like:
ORA-01114: IO error writing block to file 202 (block # 458633)
ORA-27063: skgfospo: number of bytes read/written is incorrect
SVR4 Error: 28: No space left on device
Additional information: -1
Additional information: 122880
it means that a datafile cannot extend anymore because a disk (or better said, some kind of storage device) is full.

Specifically, the high numbering of the file (in blue), refers to a temporary file.

If the number is in the range between 1 - DB_FILES, where DB_FILES is an initialization parameter whose value can be retrieved either with:

select * from v$parameter
where name = 'db_files';
or in SQL*Plus with
show parameter db_files
it means that we are talking of an ordinary datafile.

In this case DB_FILES was set to 200, which means that 202 refers to a temporary file and tempfiles are numbered starting from DB_FILES + 1.

If the temporary file is set to AUTOEXTEND ON, then the root cause of the problem can be a huge query on a view requiring a big sort operation or the insertion of lots of rows into a temporary table that made the temporary tablespace grow larger than usual until all the space on the device was used up.

See more message translations for ORA-01114, ORA-27063 and search additional resources.


ORA-01114: errore I/O scrivendo blocco su file  (blocco n. )
ORA-27063: skgfospo: il numero di byte di read/written non è corretto
ORA-01114: error de E/S al escribir el bloque en el archivo (bloque número )
ORA-27063: skgfospo: el número de bytes leídos/escritos es incorrecto
ORA-01114: error d'E/S en l'escriptura d'un bloc en el fitxer (bloc # )
ORA-27063: skgfospo: el número de bytes llegits/escrits és incorrecte
ORA-01114: erreur d'E/S en écriture de blocs dans fichier '' (bloc # )
ORA-27063: skgfospo : le nombre d'octets lus/écrits est incorrect
ORA-01114: EA-Fehler beim Schreiben von Block in die Datei (Block Nr. )
ORA-27063: skgfospo: Zahl der gelesenen/geschriebenen Bytes falsch
ORA-01114: σφάλμα ΕΕ στο γράψιμο μπλοκ στο αρχείο (αριθ. μπλοκ )
ORA-27063: skgfospo: ο αριθμός bytes που διαβάσθηκαν/γράφηκαν είναι λανθασμένος
ORA-01114: IO-fejl ved skrivning af blok i filen (blok # )
ORA-27063: skgfospo: Antal læste/skrevne byte er forkert
ORA-01114: IO-fel vid lagring av block i filen (blocknr )
ORA-27063: skgfospo: antalet byte som lästs/lagrats är felaktigt
ORA-01114: I/U-feil ved skriving av blokk til filen (blokk nr )
ORA-27063: skgfospo: feil antall byte lest/skrevet
ORA-01114: IO-virhe tiedoston lohkon kirjoituksessa (lohkonro )
ORA-27063: skgfospo: kirjoitettujen/luettujen tavujen määrä on virheellinen
ORA-01114: IO hiba a(z) fájl blokkjának írása közben (blokk: # )
ORA-27063: skgfospo: a kiírt vagy a beolvasott bájtok száma nem egyezik
ORA-01114: eroare de intrare/ieşire la scrierea blocului în fişierul (bloc nr. )
ORA-27063: skgfospo: numărul de octeţi citiţi/scrişi este incorect
ORA-01114: IO-fout bij schrijven van blok naar bestand (bloknummer ).
ORA-27063: skgfospo: aantal bytes lezen/schrijven is onjuist.
ORA-01114: erro de IO durante gravação do bloco no arquivo (bloco # )
ORA-27063: skgfospo: número incorreto de bytes lidos/gravados
ORA-01114: erro de E/S ao escrever bloco no ficheiro (nº bloco )
ORA-27063: skgfospo: número de bytes lidos/gravados é incorrecto
ORA-01114: ошибка ввода/вывода при записи блока в файл (блок # )
ORA-27063: skgfospo: неверное число прочитанных/записанных байт
ORA-01114: chyba vstupu/výstupu při zápisu bloků do souboru (číslo bloku )
ORA-27063: skgfospo: počet načtených nebo zapsaných bajtů je nesprávný
ORA-01114: IO chyba pri zápise blokov do súboru (blok # )
ORA-27063: skgfospo: počet načítaných/zapísaných bajtov je nesprávny
ORA-01114: błąd We-Wy przy zapisie bloku do pliku (blok nr )
ORA-27063: skgfospo: niepoprawna liczba odczytanych/zapisanych bajtów
ORA-01114: dosyasına blok yazmada GÇ hatası (blok no )
ORA-27063: skgfospo: okunan/yazılan bayt sayısı doğru değil

Monday, June 04, 2007

Tip's Corner: counting how many times a character is in a string

Believe it or not, among the dozens of Oracle's built-in SQL functions, there is none for counting the occurrences of a specific character in a source string.

The lack of this function forced me to come up with a quick and dirty solution:
select
:str as "string",
:chr as "character",
length(:str) - length(translate(:str,chr(0)||:chr,chr(0))) as "count"
from dual;


stringcharactercount
AB^CDE^FGHI^JK^^LMNOPQ^RST^UVWX^YZ^8

Note: this works on the assumption that you are never going to count character zero.

Anyone else with a better/faster idea?

Monday, May 28, 2007

simple pager for Apex 3.0

Although i can claim that in the last 25 years i never got a phone call in the middle of the night for job reasons, i like to know as soon as possible when a major problem has occurred, especially if the error condition prevents users from doing their work.

However, if the application users are anonymous visitors of a web site, it can be difficult, if not impossible, to be contacted timely in case of problems that may arise for a variety of reason, including bugs at different levels, service outages, networking problems, unexpected usage patterns and so on.

If you are an Oracle Application Express developer or administrator you know that in the Monitor Activity section there is a useful built-in report displaying a list of unhandled exceptions that have occurred in the specified time frame, set by default to 1 day.

This report covers errors in the Apex domain, so we won't find any entries if the http server is down, that is when a user got a HTTP-404 error, but we will find entries if an Apex user got a broken report caused by an illegal parameter value, when an Apex process failed and the error was not trapped inside the program, including many other database related issues like a constraint violation or an exceeded tablespace quota that prevented the successful termination of a DML statement, always speaking in the context of Apex applications.

Clearly this approach implies an active participation on our side, because we must explicitly go to the Monitor Activity Page and run the report to know the information, and we know how easy is to forget a task when we have a busy agenda.

What I'm going to suggest here is a more passive approach that lets the computer do the work for us, that is in the end why these damned machines were invented in the first place.

The idea is to have a scheduled job that collects the information and sends us simple reports in form of emails at regular intervals.

Moreover, if you are using a mobile service provider integrating email and SMS, you can easily set up an automated paging system as i did.

And now for the real stuff.

As of Oracle Application Express version 3.0, we have finally all the required bits to carry out this task using supported objects, therefore we no longer need to have special privileges granted for accessing internal objects, in other words the most typical and least appreciated phrase in IT's history: "with the next software release you'll able to do that" has been fulfilled eventually.

Keeping in mind that that Oracle Application Express can still run on top of Oracle 9.2, where DBMS_SCHEDULER is not available, but DBMS_JOB is, i wrote a simple package that allows you to monitor the situation at regular intervals.

Of course the usual disclaimers apply, if you run a nuclear plant don't look at me if anything goes wrong with this program.

Note also that if you (or your DBA) revoked the execution privilege on DBMS_JOB package from PUBLIC (or from the schema user you are trying to compile the package into), the package compilation will fail.
If DBMS_JOB is disabled, that is parameter JOB_QUEUE_PROCESSES is set to zero (0), the pager will not be started, informing you via DBMS_OUTPUT that background jobs are disabled.

The procedures communicate messages via DBMS_OUTPUT and, hopefully, are self-explanatory.

I strongly recommend to perform these tasks from within the Apex environment, but you can do the same from sqlplus too, remembering to COMMIT whenever necessary.

The installation is straightforward, just download the SIMPLE_PAGER for Oracle Application Express 3.0 package and compile it in you schema.

Usage notes are provided in the package specification, however, once the packages have been compiled in the schema, the pager can be started within the next minute as follows:

begin
simple_pager.start_pager(
email => '...', -- comma separated list of recipients
email_from => '...', -- email of the sender
interval => 4 -- hours between each alert
);
end;
For instance if you prefer to receive the notifications periodically, on the hour, every 2 hours, starting from 12:00:00 (supposing now it's some time between 00:00 and 11:59:59), then you can specify the optional parameter next_alert, as follows:
begin
simple_pager.start_pager(
email => '...', -- comma separated list of recipients
email_from => '...', -- email of sender
interval => 2, -- hours between each alert
next_alert => trunc(sysdate) + 1/2 -- start at noon
);
end;

The pager can run without problems in a hosted environment, where each different workspace/user can run its own pager without interfering with others.

Hope it helps.

See more articles about Oracle Application Express or download tools and utilities.

Friday, May 04, 2007

PLS-00801 internal error and ORA-06544 pl/sql internal error

After giving the finishing touches to a PL/SQL procedure that had been haunting me for the last days, consisting primarily of one BIG implicit cursor with several subqueries and a convoluted logic, i was finally ready to compile the program when i got this terrifying error message:

Line: 1 Column: 3 Error: PLS-00801: internal error [ph2csql_strdef_to_diana:bind]
Line: 7 Column: 12 Error: PL/SQL: ORA-06544: PL/SQL: internal error, arguments: [ph2csql_strdef_to_diana:bind], [], [], [], [], [], [], []
Line: 7 Column: 5 Error: PL/SQL: SQL Statement ignored

Once i recovered from the shock, i soon realized that there must be some junk i left in the SQL statement that i was converting from the sqlplus format (where i developed and tested it) into a programmatic format, that is replacing substitution variables with procedure parameters and variables.

From my past experience i know that when Oracle 9i complaints with a PL/SQL parser internal error, although the manual suggests to call Oracle Support and report a supposed bug, usually it's much ado about nothing, it's just a matter of a construct that the parser doesn't like.

Note that the parser doesn't point us to the precise spot where it found a problem, but it reports it as a generic problem regarding the whole SQL statement (line 7, where my UPDATE begins) , so it's up to us to find out where the offending bit is located.
...
begin
...
UPDATE -- at line 7
...
AND L1.TYPE_CODE = 'MK' -- at line 25
AND L2.TYPE_CODE = 'RE'
AND AM.MD_NBR = &num_model_nbr
AND L1.LOC_CODE = L2.LOC_CODE
AND AM.MD_CODE = 'WK'
AND MOG.USER_ID = i_var_user
AND MOG.CONFIG_FLAG = 'X'
AND MOG.MKT_NBR = L2.LOC_NBR
AND MOG.PL_NBR = AM.PL_NBR
...
end;

In my case, it was just a sqlplus substitution variable that i forgot to replace with its corresponding parameter, however i remember i saw this happen more than once with certain SQL statements involving function THE (aka TABLE function) inside triggers.

If you want to artificially recreate this problem, try to execute the following anonymous PL/SQL block:
begin
update dual
set k = k
where k = &test;
end;
In conclusions there are situations where the PL/SQL parser seems to say: "hey look, i know there is a problem with your SQL but i can't make out exactly what it is, i give up, sorry!"

The good news is that at least Oracle doesn't freeze and doesn't force you to press CTRL-ALT-DEL to restart!

See message translations for ORA-06544 and search additional resources.

Thursday, May 03, 2007

Generating rows magically - the many virtues of DUAL

I was reading one of the last issues of Oracle Magazine when i came across the following tip published by the #1 Oracle Guru, Tom Kyte:
with data
as
(select level n
from dual
connect by level <= 5) select * from data; 
if you don't like to use the WITH syntax in such a simple subquery, you can rewrite it as follows:
select n
from (select level n
from dual
connect by level <= 5);
Edited on May 14, 2007: be sure to check out the comments section, there is a comment from Knut about this sql query. Re-edited on September 3, 2007: It turns out that in Oracle 9i there is something odd with the simplified version of the query (after removing the inline view, so check out the comments for the latest version). This short piece of SQL is especially useful because i was still using the old-fashioned select * from table(function(n)) that is a technique available since Oracle 8i where, in order to spawn an arbitrary number of records on the fly, you must populate an object table with the desired number of elements. Note that this technique is still useful when the data must be retrieved in a less straightforward fashion, as it happens usually with complex business rules. However, thanks to this smart technique that takes advantage of the dual table and a sort of fake hierarchical query, you can spawn a recordset made up of n-rows and without additional objects, that is using less resources, less memory and in a simpler, faster way. Now, i was wondering how nice it would be to have a sort of parametric view, something like:
select n from nrows;
Well, if you don't mind setting up a simple package:
CREATE PACKAGE pkg_nrows
as
n   integer;
function fn_n  return integer;
end;
/

CREATE PACKAGE BODY pkg_nrows
as
function fn_n   return integer
is
begin
return n;
end;
end;
/

CREATE VIEW NROWS ( N ) AS
select level n
from dual
connect by level <= pkg_nrows.fn_n 
You can finally perform the query from view nrows, just after setting the value of the desired n value, that was defined as a packaged global variable.
begin
pkg_nrows.n := 5;
end;
/
select *
from nrows
/

N
-
1
2
3
4
5

And there you go with your freshly made n-tier dual table.

Hope it helps!


PS: as quirk of the day I'd mention the space characters handling inside pre-formatted text of Blogger's HTML editor, i can't make out how it works, it keeps trimming my spaces and line breaks randomly!

yes you can!

Two great ways to help us out with a minimal effort. Click on the Google Plus +1 button above or...
We appreciate your support!

latest articles