Oracle Call Interface --- BINDING AND DEFINING


Minule jsme se seznámili s pojmy binding a defining v OCI aplikacích. Tato kapitola znovu hovoří o základních pojmech a detailněji o různých typech vazeb a definic, které můžete v OCI aplikacích použít.

Vazby (binding)

Většina DML příkazů a nějaké dotazy vyžadují program k předávání dat Oraclu jako součást povelu SQL či PL/SQL. Taková data mohou být neměnná nebo literární data, známá v době překladu. Například následující SQL příkaz, který přidá zaměstnance do dazabáze, obsahuje několik literárů BESTRY, 2365:

INSERT INTO emp VALUES

(2365, `BESTRY', `PROGRAMMER', 2000, 20)

Takovéto natvrdo kódování příkazu, jako je tento, do aplikace by mohlo omezit jeho užitečnost. Potřebovali byste změnit příkaz a rekompilovat program v každý okamžik, kdy přidáte nového zaměstnance do databáze. K tomu, abyste udělali flexibilnější program, můžete napsat program tak, aby uživatel jednoduše mohl zadávat data za běhu.

Pokud připravujete SQL příkaz nebo blok PL/SQL, který obsahuje vstupní data, která mají být podporována za běhu, placeholders v příkazu SQL či bloku PL/SQL označí, kde pro ně bude místo. Např.:

INSERT INTO emp VALUES

(:empno,:ename,:job,:sal,:deptno).

Placeholders je možno použít pro vstupní proměnné v nějakém příkazu DELETE, INSERT, SELECT nebo UPDATE v SQL příkazu, nebo v PL/SQL bloku na nějakém místě v příkaze, kde můžete použít výraz nebo literární hodnotu. V PL/SQL mohou placeholders být použity pro výstupní proměnné. Placeholders nemohou být použity pro pojmenování objektů v Oraclu jako tabulek či sloupců.

Pro každý placeholder v SQL příkazu nebo PL/SQL bloku, musíte zavolat OCI rutinu, která váže adresu proměnné ve vašem programu s placeholderem. Jakmile se příkaz spustí, Oracle vydá data, která váš program umístil na vstup, nebo k vázání proměnných a podá je serveru s SQL příkazem. Při bindovacím kroku data nemusí být vázané proměnné. V tomto kroku se pouze specifikují adresy, datové typy a délka proměnné.

Pokud měníte pouze hodnotu vázané proměnné, není nutné vázat znovu k tomu, aby se příkaz mohl znovu spustit. Vazba je odkazem, takže dokud adresa vázané proměnné a vazbový handle je platná, je možné spustit SQL příkaz bez znovuvázání.

Pojmenované a poziční vazby

SQL příkazy v předchozí sekci jsou příkladem pojmenovaných vazeb. Každý placeholder v příkazu má přiřazené jméno (ename,...). Když je příkaz připravený a placeholders jsou asociovány s hodnotami v aplikaci, asociace je dělána jménem placeholderu použitím volání OCIBindByName() se jménem placeholderu předaného v placeholderovém parametru.

Druhý typ vazby je znám jako poziční vazba. V poziční vazbě jsou placeholders určovány jejich pozicí v příkaze. Asociace je výkonána mezi vstupní hodnotou a pozicí v placeholderu použitím volání OCIBindByPos().

Příklad z předchozí sekce může být může být také použit pro poziční vazbu. Všechny placeholders jsou volány OCIBindByPos() a jako parametr placeholderu je předáno číslo. (1 --- :empno, atd.)

Je nezbytné mít právě jedno vázání. Uvažte následující příkaz SQL, kde dotazujeme databázi na takové zaměstance, jejich odměny plat je vyšší než daná částka:

SELECT empno FROM emp

WHERE sal > :some_value

AND comm > :some_value

OCI aplikace může doplnit vazby pro tyto příkazy s jednoduchým voláním OCIBindByName k vazbě :some_value jménem. Druhý placeholder dětí vazební informaci z prvního.

OCI array interface

Data je možné Oraclu předávat různými způsoby. Je možné spustit SQL příkaz opakovaně použitím OCIStmtExecute() a podporovat vstupní hodnoty v každé iteraci. Nebo můžete použít Oracle array interface a vstupní hodnoty s jednoduchými příkazy a jednoduchými voláními OCIStmtExecute(). V tomto případě vážete pole k vstupnímu placeholderu a celé pole může být předáno naráz. Toto redukuje opakovaně prováděné věci Oraclu, když potřebujete updatovat či vkládat větší množství dat. Např. chci-li vložit 10 řádek, pak místo 10 volání OCIStmtExecute s jednou hodnotou mohu použít jedno volání OCIStmtExecute se vstupem pole, tedy například po síti to poputuje pouze jedenkrát.

Vázání placeholderů v PL/SQL

Je nutné použít OCIBindByName nebo OCIBindByPos k provedení základních vazeb. Při použití výstupních proměnných se používá volání OCIBindByName.

Kroky použité při vázání

Vázání placeholderů se děje v jednom či více krocích. Pro jednoduchý skalár či array vazbě, je nutné pouze specifikovat asociaci mezi placeholderem a daty. To je děláno pomocí OCIBindByName nebo OCIBindByPos.

Když je vázání hotové, OCI knihovny vědí, kde nalézt data (nebo pro PL/SQL kam dát data).

Příklad pro SQL:

Note: The checkerr() function evaluates the return code from an OCI application. The code for the function is listed in the section "Error Handling" on page 2-25.

...
/* The SQL statement, associated with stmthp (the statement handle)
by calling OCIStmtPrepare() */
text *insert = (text *) "INSERT INTO emp(empno, ename, job, sal, deptno)\
    VALUES (:empno, :ename, :job, :sal, :deptno)";
...

/*  Bind the placeholders in the SQL statement, one per bind handle. */
checkerr(errhp, OCIBindByName(stmthp, &bnd1p, errhp, (text *) ":ENAME",
    strlen(":ENAME"), (ub1 *) ename, enamelen+1, STRING_TYPE, (dvoid *) 0,
    (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT))
checkerr(errhp, OCIBindByName(stmthp, &bnd2p, errhp, (text *) ":JOB",
    strlen(":JOB"), (ub1 *) job, joblen+1, STRING_TYPE, (dvoid *)
    &job_ind, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT))
checkerr(errhp, OCIBindByName(stmthp, &bnd3p, errhp, (text *) ":SAL",
    strlen(":SAL"), (ub1 *) &sal, (sword) sizeof(sal), INT_TYPE,
    (dvoid *) &sal_ind, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, 
    OCI_DEFAULT))
checkerr(errhp, OCIBindByName(stmthp, &bnd4p, errhp, (text *) ":DEPTNO",
    strlen(":DEPTNO"), (ub1 *) &deptno,(sword) sizeof(deptno), INT_TYPE, 
    (dvoid *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT))
checkerr(errhp, OCIBindByName(stmthp, &bnd5p, errhp, (text *) ":EMPNO",
    strlen(":EMPNO"), (ub1 *) &empno, (sword) sizeof(empno), INT_TYPE,
    (dvoid *) 0, (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0,OCI_DEFAULT))

Příklad pro PL/SQL --- PL/SQL bloky se používají nejčastěji kvůli uloženým procedurám a funkcím. V následujícím příkladu program předává číslo zaměstnance a zvýšení platu na vstup uložené proceduře raised_salary.

/* Define PL/SQL statement to be used in program. */
text *give_raise = (text *) "BEGIN\
                  RAISE_SALARY(:emp_number,:sal_increase, :new_salary);\
                     END;";
OCIBind  *bnd1p = NULL;                      /* the first bind handle */
OCIBind  *bnd2p = NULL;                     /* the second bind handle */
OCIBind  *bnd3p = NULL;                      /* the third bind handle */

static void checkerr();
sb4 status;

main()
{
  sword    empno, raise, new_sal;
  dvoid    *tmp;
  OCISession *usrhp = (OCISession *)NULL; 
...
/* attach to database server, and perform necessary initializations
and authorizations */
...
      /* allocate a statement handle */
  checkerr(errhp, OCIHandleAlloc( (dvoid *) envhp, (dvoid **) &stmthp,
           OCI_HTYPE_STMT, 100, (dvoid **) &tmp));

      /* prepare the statement request, passing the PL/SQL text
        block as the statement to be prepared */
checkerr(errhp, OCIStmtPrepare(stmthp, errhp, (text *) give_raise, (ub4) 
      strlen(give_raise), OCI_NTV_SYNTAX, OCI_DEFAULT));

      /* bind each of the placeholders to a program variable */
 checkerr( errhp, OCIBindByName(stmthp, &bnd1p, errhp, (text *) ":emp_number",
             -1, (ub1 *) &empno,
            (sword) sizeof(empno), SQLT_INT, (dvoid *) 0,
             (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT));

 checkerr( errhp, OCIBindByName(stmthp, &bnd2p, errhp, (text *) ":sal_increase",
             -1, (ub1 *) &raise,
             (sword) sizeof(raise), SQLT_INT, (dvoid *) 0,
             (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT));

      /* remember that PL/SQL OUT variable are bound, not defined */

checkerr( OCIBindByName(stmthp, &bnd3p, errhp, (text *) ":new_salary",
             -1, (ub1 *) &new_sal,
             (sword) sizeof(new_sal), SQLT_INT, (dvoid *) 0,
             (ub2 *) 0, (ub2) 0, (ub4) 0, (ub4 *) 0, OCI_DEFAULT));

      /* prompt the user for input values */
printf("Enter the employee number: ");
scanf("%d", &empno); 
      /* flush the input buffer */
myfflush();

printf("Enter employee's raise: ");
scanf("%d", &raise);
      /* flush the input buffer */
myfflush();

  /* execute PL/SQL block*/
  checkerr(errhp, OCIStmtExecute(svchp, stmthp, errhp, (ub4) 1, (ub4) 0, 
      (OCISnapshot *) NULL, (OCISnapshot *) NULL, OCI_DEFAULT));

  /* display the new salary, following the raise */
printf("The new salary is %d\n", new_sal);
}

Pokročilejší vazby

V předchozích případech byly ukázány jednoduché skalární vazby. V tomto případě je nutné pouze jednoduché bind call. V některých případech další bind calls jsou nutné k definování specifických atributů pro specifické vázání datových typů a spustitelných modů.

V této sekci probereme operace pokročilejších vazeb, včetně vícekrokových vazeb a vazeb pojmeonvaných dat a REFs.

Statické Array vazby

Statické array vazbové atributy jsou používány v OCI polích struktur vazebním voláním OCIBindArrayOfStruct(). Toto volání následuje volání OCIBindByName nebo OCIBindByPos. Volání OCIBindArrayOfStruct je také použité k definování skip parametrů potřebných, pokud aplikace využívá pole struktur (tuto funkcionalitu).

Vázání LOBů

Při práci s LOBy jsou vázány LOB locators. Jeden locator nebo pole locators může být vázáno v jednom vazebním volání. Aplikace musí předat adresu LOB locatoru a ne locator sám.

Vázání v OCI_DATA_AT_EXEC módu

Pokud je parametr módu ve volání OCIBindByName() nebo v OCIBindByPos() nastaven na OCI_DATA_AT_EXEC, pak další volání OCIBindDynamic() je nezbytné, pokud aplikace bude používat callback metodu pro poskytnutí dat za běhu. Volání OCIBindDynamic nastaví callback rutinu, pokud je to nutné, pro indikaci data či kusu, který má být poskytnut.

Pokud se váže RETURN hlavní proměnné, aplikace musí používat OCI_DATA_AT_EXEC mod a musí poskytovat callbacky.

Shrnutí informací o bindingu

Type of Bind   Bind Datatype   Notes  

Scalar  

any scalar datatype  

Bind a single scalar using OCIBindByName() or OCIBindByPos().  

Array of Scalars  

any scalar datatype  

Bind an array of scalars using OCIBindByName() or OCIBindByPos().  

Named Data Type  

SQLT_NTY

 

Two bind calls are required:

  • OCIBindByName() or OCIBindByPos()
  • OCIBindObject()
 

REF  

SQLT_REF

 

Two bind calls are required:

  • OCIBindByName() or OCIBindByPos()
  • OCIBindObject()
 

LOB  

SQLT_BLOB

SQLT_CLOB  

Allocate the LOB locator using OCIDescriptorAlloc(), and then bind its address (OCILobLocator **) with OCIBindByName() or OCIBindByPos(), using one of the LOB datatypes.  

Array of Structures

or Static Arrays  

varies  

Two bind calls are required:

  • OCIBindByName() or OCIBindByPos()
  • OCIBindArrayOfStruct()
 

Piecewise Insert  

varies  

OCIBindByName() or OCIBindByPos() is required. The application may also need to call OCIBindDynamic() to register piecewise callbacks.  

REF CURSOR variables  

SQLT_RSET  

Allocate a statement handle, OCIStmt, and then bind its address (OCIStmt **) using the SQLT_RSET datatype.  

Definování (defining)

Dotazy vrací data z databáze vaší aplikaci. Když probíhá výpočet dotazu, musíte definovat výstupní proměnné nebo pole výstupních proměnných pro každou položku v seznamu za selectem, ze kterého chceme získat data. Definovací krok vytváří asociaci, která určuje, kam se vrácené výsledky ukládají a v jakém formátu.

Například ve vašem příkazu OCI zpracováváte následující příkaz:

SELECT name, ssn FROM employees

WHERE empno = :empnum

Normálně potřebujete definovat dvě výstupní proměnné, jednu k příjmu dat ze sloupce name, druhou ze sloupce ssn. (Pokud bychom se zajímalz pouze o vrácené hodnoty ze sloupce name, nemuseli bychom definovat výstupní proměnnou pro ssn.

Pokud zpracovávaný příkaz SELECT má vrátit více než jednoduchou hodnotu rpo dotaz, výstupní proměnné, které definujete, musí být pole místo skalární proměnné.

OCI zpracovává definiční volání lokálně, na straně klienta. Navíc k indikaci místa bufferů, kam mají být uloženy výsledky, definiční krok také určuje, které typy datových konverzí, pokud nějaké, se mají provést, když jsou data vrácena aplikaci.

Kroky užívané při definování

Definování výstupních proměnných je prováděno v jednom nebo více krocích. Základní definování je uskutečňováno s OCI definicemi pozičním voláním, OCIDefineByPos(). Tento krok vytváří asociaci mezi položkami ze select-seznamu a výstupními proměnnými. Další definiční volání může být nezbytné pro jisté datové typy nebo fetch mody.

Je-li krok define úplný, OCI knihovny znají, kam získaná data dát po fetchingu z databáze.

Následující příklad kódu ukazuje skalární výstup proměnné, která má být definována následovaná spuštěním a popisem:

/* The following statement was prepared, and associated with statement
      handle stmthp1.

      SELECT dname FROM dept WHERE deptno = :dept_input

      The input placeholder was bound earlier, and the data comes from the
      user input below */

printf("Enter employee dept: ");
      scanf("%d", &deptno);
      myfflush();

/* Execute the statement. If OCIStmtExecute() returns OCI_NO_DATA, meaning that 
no data matches the query, then the department number is invalid. */
            if ((status = OCIStmtExecute(svchp, stmthp1, errhp, 1, 0, 0, 0, 
                      OCI_DEFAULT))
          && (status != OCI_NO_DATA))
      {
        checkerr(errhp, status);
        do_exit(EXIT_FAILURE);
      }  
      if (status == OCI_NO_DATA) {
        printf("The dept you entered doesn't exist.\n");
                return 0;
        }
/* The next two statements describe the select-list item, dept, and
      return its length */
checkerr(errhp, OCIParamGet(stmthp1, errhp, &parmdp, (ub4) 1));
checkerr(errhp, OCIAttrGet((dvoid*) parmdp, (ub4) OCI_DTYPE_PARAM, 
       (dvoid*) &deptlen, (ub4 *) 0, (ub4) OCI_ATTR_DATA_SIZE, 
       (OCIError *) errhp  ));

/* Use the retrieved length of dept to allocate an output buffer, and
      then define the output variable. If the define call returns an error,
      exit the application */
    dept = (text *) malloc((int) deptlen + 1);
  if (status = OCIDefineByPos(stmthp1, &defnp, errhp,
             1, (ub1 *) dept, deptlen+1,
             SQLT_STRING, (dvoid *) 0,
             (ub2 *) 0, OCI_DEFAULT))
  {
    checkerr(errhp, status);
    do_exit(EXIT_FAILURE);
  }

Pokročilejší definice

V některých případech definiční krok vyžaduje více než pouhé volání OCIDefineByPos(). Existují další volání, která definují atributy polí fetch (OCIDefineArrayOfStruct()) nebo pojmenované datové typy fetch (OCIDefineObject()). Například nafetchovat vícero řad se sloupcem pojmenovaných datových typů, věchna tři volání musí být vyvolána pro sloupec, ale nafetchovat vícero čad se skalárními sloupci, OCIDefineArrayOfStruct a OCIDefineByPos jsou nezbytné.

Následující část rozebírá týkající se různých typů a definic.

Definice LOB výstupních proměnných

Pro LOBy, buffer pointer musí být locator typu OCILobLocator, alokovaný pomocí volání OCIDescriptorAlloc. LOB locators a ne LOBové hodnoty, jsou vždy vráceny pro sloupek LOB. LOB proměnné mohou pak být nafetchovány použití OCI LOB volání na fetched locator.

Definice PL/SQL výstupních proměnných

Nepoužívejte definiční volání k definování výstupních proměnných pro položky ze seznamu select v SQL SELECT příkazu v PL/SQL bloku. Místo toho je nutné použít OCI vazební volání.

Definice pro piecewise fetch

Pokud provádíte piecewise fetch, iniciální volání OCIDefineByPos je vyžadováno! Další volání OCIDefineDynamic je nezbytné, pokud aplikace bude používat callbacky a ne standarní polling mechanismy k fetchování dat.

Definice polí struktur

Užíváme-li pole struktur, je vyžadováno iniciálné volání OCIDefineByPos. Volání OCIDefineArrayOfStruct je nezbytné k nastavení dalších parametrů, včetně skip parametru nezbytné pro operace s poli struktur.

Pole struktur

Funkcionalita polí struktur v Oracle8 OCI může jednoduše zpracovávat vícero řad, a sloupců, tj. provádět nad nimi operace. OCI programátor může vytvořit strukturu datových položek relačních skalárů a pak načítat hodnoty z databáze do pole těchto struktur a vkládat hodnoty do databáze z pole těchto struktur.

Skip parametry

Pokud rozdělíte sloupce dat napříč polem struktur, není to více spojité. Jednoduché pole struktur uchovává data, ačkoli bylo složeno z několika polí skalárů. Proto vývojáři musí specifikovat "skip parametr" pro každé pole, které vážou a definují. Tento skip parametr specifikuje počet bytů, které potřebují k přeskočení v poli struktur předtím, než bylo totéž pole znovu spočteno. Potom je to ekvivalentní bytovému rozměru jedné struktury. Následující obrázek ukazuje, jak je určen skip parametr. V tomto případě je skip parametr součet rozměrů polí field1, field2 a field3, což je 8 bytů. To je rovné rozměru jedné struktury.

Na některých systémech může být nezbytné nastavit skip parametr na sizeof(jeden prvek pole) raději než na sizeof(struktura)- Protože některé kompilátory mohou cosi do struktury vkládat.

Skip parametry pro standardní pole

Schopnost pracovat s poli struktur je rozšíření funkcionality vázání a definování polí programových proměnných. Programátoři mohou pracovat také se standardními polemi (oproti s polemi struktur). Pokud se specifikuje operace se standardním polem, přístulšný skip musí být roven rozměru typu dat uvažovaného pole. Např. pole deklarované jako text emp_name[4][20] skip parametr pro každé vázání či definování je 20. Všechny prvky pole jsou chápány jako oddělené jednotky, ne části struktur.

OCI volání používaná s polemi struktur

Musí být použita dvě OCI volání: OCIBindArrayOfStruct (pro vázání fields v polích struktur pro vstupní proměnné) a OCIDefineArrayOfStruct (pro definici polí struktur pro výstupní proměnné).

Pole struktur a indikátorové proměnné

Implementace polí a struktur také podporuje použití indikátorových proměnných a návratových kódů. OCI aplikační vývojáři mohou deklarovat paralelní pole indikátorových proměnných na úrovni slopců a návrátové kódy, související s polemi informací, které mají být fetchovýny, insertovány nebo updateovány. Tato pole mají vlastní skip parametry, které jsou specifikovány v průběhu volání OCIBindArrayOfStruct nebo OCIDefineArrayOfStruct.

DML s klausulí RETURNING

OCI podporuje použití RETURNING s SQL příkazy INSERT, UPDATE, DELETE. Použitím RETURNING calusule s DML příkazy dovoluje kombinovat dva SQL příkazy v jednom, a tedy ochraňuje vás to před serverovým roudn-trip. V OCI jsou hodnoty vráceny aplikacím skrze použití OUT bind proměnných...

Error handling

Vázací funkce OCIBindDynamic musí být připravena přijmout částečné výsledky příkazu v případě výskytu chyby. Např. volám 10 x DML příkaz a v páté iteraci nastane chyba. Server mi vrátí 4 iterace. V tomto případě funkce callback může být volána k přijetí dat pro první 4 iterace.

Další poznámky ke callbackům.

Pokud je zavolána callback funkce, atribut OCI_ATTR_ROWS_RETURNED bind handlu říká aplikaci počet řádků, které mají být vráceny v jednotlivé iteraci. Proto, pokud je zavolán callback poprvé v určité iteraci (např. index=0), uživatel může alokovat místo pro všechny řádky, které budou vráceny pro tamty vázané proměnné. když je callback subsekvenčně zavolán s indexem > 0 v téže iteraci, uživatel může pouze zvětšit bufferpointer ke správné paměti uvnitř alokačního prostoru k získání dat.