Post on 22-Nov-2021
transcript
[Otto Gold, ORA2]
Stringy, Kurzory a Výjimky
Stringy a VARCHARy
VARCHAR formálně
VARCHAR - jednoduchá strukturu (struct) jazyka CStruct{ unsigned short len; //délka obsahu unsigned char arr[20]; //hodnota obsahu}
Výhody použití VARCHAR
Lze explicitně použít délku řetězce v programových konstrukcích
printf("Username is %.*s\n", username.len, username.arr);
Nejčastěji: dodat si nulu na konec řetězce, aby byl pro C null-terminated
username.arr[username.len] = ’\0’;
Délka VARCHARu
Délku je nutno explicitně specifikovat při deklaraci (rozsah je 1..65533)
Příklad chyby: VARCHAR null_string[]; /* invalid */ Délkou se inicializuje VARCHAR.len Specifikace délky nemusí byt pouze explicitní číslo,
ale cokoli co lze vyčíslit na int v době práce preprocesoru (#defined macro, jiný rozumný výraz)
Použití VARCHARu (1)
...int part_number;VARCHAR part_desc[40];...main(){...EXEC SQL SELECT pdesc INTO :part_descFROM partsWHERE pnum = :part_number;...Bezprostředně po provedení obsahuje part_desc.len
délku získaného řetězce a part_desc.arr samotný řetězec (neukončený nulou)
Použití VARCHARu (2)
Takhle to pak vypadá v samotném C
printf("\n\nEnter part description: ");gets(part_desc.arr);
/* You must set the length of the stringbefore using the VARCHAR in an INSERT or UPDATE */
part_desc.len = strlen(part_desc.arr);
VARCHAR a NULL (Out)
Oracle nastavuje délku VARCHARU automaticky Jenže, jestliže je výsledkem SELECTu nebo
FETCHe NULL, tak server délku nenastavuje (nedojde ke změně)
Jestliže je výsledkem NULL a nezjišťujeme to indikátorovou proměnnou, skončíme s chybou
(Neošetřovat a vyhnout se chybě lze takto: nastavíme UNSAFE_NULL=YES)
VARCHAR a NULL (In)
NULL uložíme do DB tak, že délku VARCHARu nastavíme na 0
Jestliže sloupec nemá povolenu hodnotu NULL, skončím s chybou
Předávání VARCHARu do fce
V Pro*C/C++ předáváme referencí
VARCHAR emp_name[20];VARCHAR *name;emp_name.len = 20;SELECT ename INTO :emp_name FROM emp WHERE empno =
7499;print_employee_name(&emp_name);print_employee_name(name)
printf("name is %.*s\n", name->len, name->arr);
Kurzorové proměnné
Co to je a k čemu to je
Je to handle na kurzor definovaný a otevřený na ORA serveru, v PL/SQL
Udržovatelnost – jde pouze o „zástupce“ pro kurzor definovaný jen jednou v PL/SQL, takže kdekoli se používá beze změn, změna která plní kurzor daty se dělá jen na jednom místě
Bezpečnost – lze omezit uživatele, např. Smí číst kurzor ale nesmí číst tabulky, ze kterých se plní
Deklarace kurzorové proměnné
Pseudotyp SQL_CURSOR, běžná proměnná POZOR: dodržovat case (CASE, ne CaSe)
EXEC SQL BEGIN DECLARE SECTION;sql_cursor emp_cursor; /* a cursor variable */SQL_CURSOR dept_cursor; /* a cursor variable */sql_cursor *ecp; /* a pointer to a var */EXEC SQL END DECLARE SECTION;
/* assign a value to the pointer */ecp = &emp_cursor;
Alokace kurzorové proměnné
Kurzor na serveru je třeba otevřít (nelze spoléhat na embedded SQL OPEN)
Otevřít lze 2 způsoby: Voláním PL/SQL procedury Použitím anonymního bloku přímo v
preprocesoru
Ukázka (1) – PL/SQL balík
CREATE PACKAGE demo_cur_pkg ASTYPE EmpName IS RECORD (name VARCHAR2(10));TYPE cur_type IS REF CURSOR RETURN EmpName;PROCEDURE open_emp_cur (curs IN OUT cur_type,
dept_num IN NUMBER);END;
CREATE PACKAGE BODY demo_cur_pkg ASCREATE PROCEDURE open_emp_cur (curs IN OUT cur_type,
dept_num IN NUMBER) ISBEGIN
OPEN curs FORSELECT ename FROM empWHERE deptno = dept_numORDER BY ename ASC;
END; END;
Ukázka (2) - použití
sql_cursor emp_cursor;char emp_name[11];/* allocate the cursor variable */EXEC SQL ALLOCATE :emp_cursor; /* Open the cursor on the server side. */EXEC SQL EXECUTEdemo_cur_pkg.open_emp_cur(:emp_cursor, :dept_num);
EXEC SQL WHENEVER NOT FOUND DO break;for (;;){
EXEC SQL FETCH :emp_cursor INTO :emp_name;printf("%s\n", emp_name);
}
Použití – anonymní blok
sql_cursor emp_cursor;int dept_num = 10;EXEC SQL EXECUTEBEGINOPEN :emp_cursor FOR SELECT ename FROM emp WHERE deptno = :dept_num;
END;END-EXEC;
Použití – embedded SQL
sql_cursor emp_cursor;EXEC ORACLE OPTION(select_error=no);EXEC SQLSELECT CURSOR(SELECT ename FROM emp WHERE deptno =
:dept_num)INTO :emp_cursor FROM DUAL;EXEC ORACLE OPTION(select_error=yes);
Poznámka: (select_error) Před dotazy využívajícími kurzory je třeba vypnout hlášení chyb. Předejdeme tak zrušení rodičovského kurzoru a tedy chybám v programu.
Uzavření a uvolnění
Příkaz CLOSE pro zavření proměnné /* hostitelská proměnná --> s dvojtečkou*/ EXEC SQL CLOSE :emp_cursor; Odalokace pomocí EXEC SQL FREE :emp_cursor; Důležité: Při odpojení a znovu připojení k
DB je třeba realokovat (ALLOCATE) všechny kurzory
Vybraná omezení pro kurzory
Lze použít jen příkazy ALLOCATE, FETCH, FREE a CLOSE
DECLARE CURSOR s kurzorovými proměnnými nijak nepracuje (PLSQL)
Nelze FETCHovat z proměnné co je CLOSED Analogicky z ne-ALLOCATEd proměnné Ve vybraných módech je chyba zavřít zavřený kurzor Kurzorové proměnné nelze ukládat do DB sloupců Kurzorovou proměnnou nelze psát do PL/SQL kódu,
pouze typ pro odpovídající kurzor ...a další
Zpracování chyb
Error handling - přehled
Průběžné testování SQLSTATE nebo SQLCODE (integer)
SQL Communications Area (cqlca) – update po jakékoli proveditelné akci na serveru Varianta explicitní: ruční kontrola sqlca Implicitní: WHENEVER clause
Extra informace: struktura ORACA (contains cursor statistics, information about the current SQL statement, option settings, and system statistics)
Proměnná SQLSTATE
Použití SQLCA je volitelné, ale SQLSTATE je povinné
Dle normy SQL92 se definuje SQLCODE, který je podobný
Obsahuje informace o úspěchu nebo o výjimce SQL92 definuje všechny obvyklé SQL výjimky SQLCODE informuje pouze o chybách,
SQLSTATE o chybách i varováních, STATE je tedy preferovaná varianta kontroly
SQLSTATE detaily
/* SQLSTATE must be declared with a dimension of exactly 6 characters, Upper case is required */
char SQLSTATE[6]; Co obsahuje:
2 znaky – class code (00 = success) 3 znaky – subclass code
Příklad: 22012 22 (data exception) 012 (division by zero)
SQLCODE
Je vyžadován, jestliže nepoužíváme SQLSTATE Lze deklarovat více než jeden SQLCODE, každý
se specifickým rozsahem platnosti Jestliže nedeklarujeme SQLCA, vytvoří se
implicitní, nám nepřístupné Jestliže deklarujeme obojí, plní se stejně
/* declare status variable--must be upper case */long SQLCODE;
Použití SQLCA
Status codes (0, 1+ Exception, -1- Not executed, internal error)
Warning flags (sqlwarn[0..7]) Rows-processed count (sqlerrd[2]) Parse error offset (kde začíná parse chyba) Error message text (SQLERRMC, 70, sqlglm() )
Struct SQLCA
struct sqlca {char sqlcaid[8];long sqlabc;long sqlcode;struct{
unsigned short sqlerrml;char sqlerrmc[70];
} sqlerrm;char sqlerrp[8];long sqlerrd[6];char sqlwarn[8];char sqlext[8];
};
Direktiva WHENEVER
EXEC SQL WHENEVER <condition> <action>; Vyžaduje deklaraci SQLCA !! Kontroluje SQLCA na tyto podmínky
SQLWARNING: Sqlwarn[0] je nastaveno nebo SQLCODE má kladnou hodnotu, různou od +1403
SQLERROR: SQLCODE má zápornou hodnotu NOT FOUND: SQLCODE = +1403 (no rows)
WHENEVER akce
CONTINUE – nic, default DO – předává kontrolu error handleru DO BREAK – standardní break v LOOPu DO CONTINUE - analogicky continue GOTO label – skok na label, max 31zn STOP – konec a odrolování kde nebylo
řádně odkomitováno
WHENEVER příklad (1)
EXEC SQL WHENEVER NOT FOUND GOTO close_cursor;EXEC SQL WHENEVER SQLWARNING CONTINUE;EXEC SQL WHENEVER SQLERROR GOTO error_handler;
EXEC SQL WHENEVER SQLERROR DO handle_insert_error("INSERT error");
EXEC SQL INSERT INTO emp (empno, ename, deptno) VALUES (:emp_number, :emp_name, :dept_number);
EXEC SQL WHENEVER SQLERROR DO handle_delete_error("DELETE error");
EXEC SQL DELETE FROM dept WHERE deptno = :dept_number;
WHENEVER příklad (2)
handle_insert_error(char *stmt){ switch(sqlca.sqlcode){
case -1:/* duplicate key value */
break;case -1401:
/* value too large */break;default:
/* do something here too */break;
}}
Rady pro užití WHENEVER
Vkládat WHENEVER klauzule před první „proveditelné“ SQL operace
Ošetřujte situaci, kdy data „nejsou“ a používáte for(;;)
Ošetřujte nekonečný cyklus nebo nepoužívejte GOTO
Cíl skoku musí být korektně umístěn, analogicky error handler (--> př.)
Nekorektní umístění
func1(){EXEC SQL WHENEVER SQLERROR GOTO labelA;EXEC SQL DELETE FROM emp WHERE deptno = :dept_number;...labelA:...}func2(){EXEC SQL INSERT INTO emp (job) VALUES (:job_title);...}