<?xml version="1.0" encoding="UTF-8"?>
<!-- EN-Revision: 21740 -->
<!-- Reviewed: no -->
<sect1 id="zend.db.adapter">
    <title>Zend_Db_Adapter</title>

    <para>
        <classname>Zend_Db</classname> i powiązane z nim klasy udostępniają prosty interfejs
        dla baz danych <acronym>SQL</acronym> z poziomu Zend Framework. Klasa 
        <classname>Zend_Db_Adapter</classname> jest podstawowym komponentem potrzebnym by
        połączyć aplikację PHP z systemem zarządzania bazą danych (<acronym>RDBMS</acronym>).
        Dla każdego rodzaju bazy danych istnieje oddzielna klasa adaptera 
        <classname>Zend_Db</classname>.
    </para>

    <para>
        Adaptery <classname>Zend_Db</classname> tworzą połączenie pomiędzy rozszerzeniami 
        <acronym>PHP</acronym> dla konkretnych rodzajów baz danych a wspólnym interfejsem,
        dzięki czemu można napisać aplikację raz i w nieskomplikowany sposób uruchamiać 
        ją z wieloma rodzajami <acronym>RDBMS</acronym>
    </para>

    <para>
        Interfejs klasy adaptera jest podobny do interfejsu rozszerzenia
        <ulink url="http://www.php.net/pdo">PHP Data Objects</ulink>. <classname>Zend_Db</classname>
        zawiera klasy adapterów dla sterowników <acronym>PDO</acronym> następujących rodzajów
        baz danych:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                IBM DB2 oraz Informix Dynamic Server (IDS), poprzez rozszerzenie
                <acronym>PHP</acronym>
                <ulink url="http://www.php.net/pdo-ibm">pdo_ibm</ulink>.
            </para>
        </listitem>

        <listitem>
            <para>
                MySQL, poprzez rozszerzenie <acronym>PHP</acronym>
                <ulink url="http://www.php.net/pdo-mysql">pdo_mysql</ulink>
            </para>
        </listitem>

        <listitem>
            <para>
                Microsoft <acronym>SQL</acronym> Server, używając rozszerzenia
                <acronym>PHP</acronym>
                <ulink url="http://www.php.net/pdo-dblib">pdo_dblib</ulink>
            </para>
        </listitem>

        <listitem>
            <para>
                Oracle, poprzez rozszerzenie <acronym>PHP</acronym>
                <ulink url="http://www.php.net/pdo-oci">pdo_oci</ulink>
            </para>
        </listitem>

        <listitem>
            <para>
                PostgreSQL, poprzez rozszerzenie <acronym>PHP</acronym>
                <ulink url="http://www.php.net/pdo-pgsql">pdo_pgsql</ulink>
            </para>
        </listitem>

        <listitem>
            <para>
                SQLite, używając rozszerzenia <acronym>PHP</acronym>
                <ulink url="http://www.php.net/pdo-sqlite">pdo_sqlite</ulink>
            </para>
        </listitem>
    </itemizedlist>

    <para>
        Dodatkowo <classname>Zend_Db</classname> dostarcza klas adapterów korzystających
        z rozszerzeń <acronym>PHP</acronym> dla następujących rodzajów
        baz danych:
    </para>

    <itemizedlist>
        <listitem>
            <para>
                MySQL, poprzez rozszerzenie <acronym>PHP</acronym>
                <ulink url="http://www.php.net/mysqli">mysqli</ulink>
            </para>
        </listitem>

        <listitem>
            <para>
                Oracle, poprzez rozszerzenie <acronym>PHP</acronym>
                <ulink url="http://www.php.net/oci8">oci8</ulink>
            </para>
        </listitem>

        <listitem>
            <para>
                IBM DB2 oraz DB2/i5, poprzez rozszerzenie <acronym>PHP</acronym>
                <ulink url="http://www.php.net/ibm_db2">ibm_db2</ulink>
            </para>
        </listitem>

        <listitem>
            <para>
                Firebird/Interbase, poprzez rozszerzenie <acronym>PHP</acronym>
                <ulink url="http://www.php.net/ibase">php_interbase</ulink>
            </para>
        </listitem>
    </itemizedlist>

    <note>
        <para>
            Każdy adapter <classname>Zend_Db</classname> używa określonego rozszerzenia
            <acronym>PHP</acronym>. Aby użyć adaptera należy mieć zainstalowane i włączone
            odpowiadające mu rozszerzenie <acronym>PHP</acronym>. Np. aby użyć dowolnego
            adaptera <acronym>PDO</acronym> <classname>Zend_Db</classname>, wymagane jest
            posiadanie włączonego rozszerzenia <acronym>PDO</acronym> oraz sterownika dla 
            danego rodzaju <acronym>RDBMS</acronym>.
        </para>
    </note>

    <sect2 id="zend.db.adapter.connecting">
        <title>Połączenie z bazą danych za pomocą adaptera</title>

        <para>
            Ta część opisuje jak tworzy się instancję adaptera bazy danych. Jest to odpowiednik
            połączenia z bazą danych w aplikacji <acronym>PHP</acronym>.
        </para>

        <sect3 id="zend.db.adapter.connecting.constructor">
            <title>Użycie konstruktora adaptera Zend_Db</title>

            <para>
                Można utworzyć instancję adaptera za pomocą konstruktora. Przyjmuje on
                pojedynczy argument - tablicę parametrów używanych do deklaracji połączenia. 
            </para>

            <example id="zend.db.adapter.connecting.constructor.example">
                <title>Użycie konstruktora adaptera</title>

                <programlisting language="php"><![CDATA[
$db = new Zend_Db_Adapter_Pdo_Mysql(array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'test'
));
]]></programlisting>
            </example>
        </sect3>

        <sect3 id="zend.db.adapter.connecting.factory">
            <title>Użycie fabryki Zend_Db</title>

            <para>
                Alternatywą w stosunku do bezpośredniego używania konstruktora jest utworzenie
                instancji adaptera za pomocą statycznej metody 
                <methodname>Zend_Db::factory()</methodname>. Ta metoda dynamicznie ładuje plik
                klasy adaptera używając
                <link linkend="zend.loader.load.class">Zend_Loader::loadClass()</link>.
            </para>

            <para>
                Pierwszy argument metody to łańcuch znaków, który wyznacza nazwę bazową klasy
                adaptera. Np. string '<classname>Pdo_Mysql</classname>' odpowiada klasie
                <classname>Zend_Db_Adapter_Pdo_Mysql</classname>. Drugi argument to ta sama
                tablica parametrów, którą należało podać adapterowi konstruktora.
            </para>

            <example id="zend.db.adapter.connecting.factory.example">
                <title>Użycie metody fabryki adaptera</title>

                <programlisting language="php"><![CDATA[
// Poniższa instrukcja jest zbędna ponieważ plik z klasą
// Zend_Db_Adapter_Pdo_Mysql zostanie załadowany przez fabrykę
// Zend_Db

// require_once 'Zend/Db/Adapter/Pdo/Mysql.php';

// Załaduj klasę Zend_Db_Adapter_Pdo_Mysql automatycznie
// i utwórz jej instancję
$db = Zend_Db::factory('Pdo_Mysql', array(
    'host'     => '127.0.0.1',
    'username' => 'webuser',
    'password' => 'xxxxxxxx',
    'dbname'   => 'test'
));
]]></programlisting>
            </example>

            <para>
                Jeśli jest zdefiniowana własna klasa dziedzicząca po
                <classname>Zend_Db_Adapter_Abstract</classname> ale jej nazwa nie zawiera
                "<classname>Zend_Db_Adapter</classname>", można użyć metody
                <methodname>factory()</methodname> do załadowania adaptera jedynie jeśli
                poda się swój prefiks nazwy adaptera z kluczem 'adapterNamespace' do
                argumentu zawierającego parametry połączenia.
            </para>

            <example id="zend.db.adapter.connecting.factory.example2">
                <title>Użycie metody fabryki dla własnej klasy adaptera</title>
                <programlisting language="php"><![CDATA[
// Nie trzeba ładować pliku klasy adaptera
// bo robi to metoda fabryki Zend_Db

// Załadowanie klasy MyProject_Db_Adapter_Pdo_Mysql automatycznie
// i utworzenie jej instancji
$db = Zend_Db::factory('Pdo_Mysql', array(
    'host'             => '127.0.0.1',
    'username'         => 'webuser',
    'password'         => 'xxxxxxxx',
    'dbname'           => 'test',
    'adapterNamespace' => 'MyProject_Db_Adapter'
));
]]></programlisting>
            </example>
        </sect3>

        <sect3 id="zend.db.adapter.connecting.factory-config">
            <title>Użycie Zend_Config z fabryką Zend_Db</title>

            <para>
                Każdy z argumentów metody <methodname>factory()</methodname> może również zostać
                podany w formie obiektu klasy <link linkend="zend.config">Zend_Config</link>.
            </para>

            <para>
                Jeśli pierwszy argument jest obiektem <classname>Zend_Config</classname>
                to wymagane jest aby posiadał właściwość o nazwie
                <property>adapter</property>, w której będzie zapisany łańcuch znaków
                określający nazwę bazową klasy adaptera. Opcjonalnie, obiekt ten może zawierać
                właściwość '<property>params</property>' z właściwościami potomnymi
                odpowiadającymi nazwom parametrów adaptera. Będą one użyte jedynie w
                przypadku braku drugiego argumentu metody
                <methodname>factory()</methodname>.
            </para>

            <example id="zend.db.adapter.connecting.factory.example1">
                <title>Użycie metody fabryki adaptera z obiektem Zend_Config</title>

                <para>
                    W poniższym przykładzie, obiekt <classname>Zend_Config</classname>
                    jest utworzony z tablicy.
                    Można również załadować dane z pliku zewnętrznego poprzez klasy
                    <link linkend="zend.config.adapters.ini">Zend_Config_Ini</link> oraz
                    <link linkend="zend.config.adapters.xml">Zend_Config_Xml</link>.
                </para>

                <programlisting language="php"><![CDATA[
$config = new Zend_Config(
    array(
        'database' => array(
            'adapter' => 'Mysqli',
            'params'  => array(
                'host'     => '127.0.0.1',
                'dbname'   => 'test',
                'username' => 'webuser',
                'password' => 'secret',
            )
        )
    )
);

$db = Zend_Db::factory($config->database);
]]></programlisting>
            </example>

            <para>
                Drugi argument metody <methodname>factory()</methodname> może stanowić tablicę
                asocjacyjną zawierającą wartości odpowiadające parametrom adaptera. Ten argument 
                jest opcjonalny. Jeśli pierwszy argument jest obiektem klasy 
                <classname>Zend_Config</classname> to powinien zawierać wszystkie parametry a
                drugi argument jest ignorowany. 
            </para>
        </sect3>

        <sect3 id="zend.db.adapter.connecting.parameters">
            <title>Parametry adaptera</title>

            <para>
                Poniższa lista opisuje parametry wspólne dla klas adapterów 
                <classname>Zend_Db</classname>.
            </para>

            <itemizedlist>
                <listitem>
                    <para>
                        <emphasis>host</emphasis>:
                        łańcuch znaków zawierający nazwę hosta lub adres IP serwera bazy danych.
                        Jeśli baza danych jest uruchomiona na tej samej maszynie co aplikacja to
                        można tu umieścić 'localhost' lub '127.0.0.1'.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>username</emphasis>:
                        identyfikator użytkownika używany do autoryzacji połączenia z serwerem 
                        bazy danych.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>password</emphasis>:
                        hasło użytkownika używane do autoryzacji połączenia z serwerem 
                        bazy danych.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>dbname</emphasis>:
                        nazwa instancji bazy danych na serwerze.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>port</emphasis>:
                        niektóre serwery bazy danych używają do komunikacji numeru portu określonego 
                        przez administratora. Ten parametr pozwala na ustawienie numeru portu przez 
                        który aplikacja <acronym>PHP</acronym> się łączy tak aby zgadzał się z tym
                        ustawionym na serwerze bazy danych.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>charset</emphasis>:
                        określenie zestawu znaków używanego podczas połączenia.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>options</emphasis>:
                        ten parametr to tablica asocjacyjna opcji obsługiwanych przez wszystkie
                        klasy <classname>Zend_Db_Adapter</classname>.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>driver_options</emphasis>:
                        ten parametr to tablica asocjacyjna zawierająca dodatkowe opcje
                        specyficzne dla każdego rozszerzenia bazy danych. Typowym przykładem
                        użycia tego parametru jest zbiór atrybutów sterownika
                        <acronym>PDO</acronym>.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        <emphasis>adapterNamespace</emphasis>:
                        początkowa część nazwy klasy używanego adaptera, używana zamiast
                        '<classname>Zend_Db_Adapter</classname>'. Przydatna w przypadku użycia
                        metody <methodname>factory()</methodname> do załadowana własnej klasy
                        adaptera.
                    </para>
                </listitem>
            </itemizedlist>

            <example id="zend.db.adapter.connecting.parameters.example1">
                <title>Przekazanie do fabryki opcji zmiany wielkości znaków</title>

                <para>
                    Opcję tą można podać za pomocą stałej
                    <constant>Zend_Db::CASE_FOLDING</constant>.
                    Odpowiada ona atrybutowi <constant>ATTR_CASE</constant> w <acronym>PDO</acronym>
                    oraz rozszerzeniu IBM DB2, który zmienia wielkość znaków w nazwach kolumn 
                    zwracanych w rezultacie zapytania. Opcja przybiera wartości
                    <constant>Zend_Db::CASE_NATURAL</constant> (bez zmiany - domyślnie),
                    <constant>Zend_Db::CASE_UPPER</constant> (zmiana na wielkie znaki) oraz
                    <constant>Zend_Db::CASE_LOWER</constant> (zmiana na małe znaki).
                </para>

                <programlisting language="php"><![CDATA[
$options = array(
    Zend_Db::CASE_FOLDING => Zend_Db::CASE_UPPER
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'options'        => $options
);

$db = Zend_Db::factory('Db2', $params);
]]></programlisting>
            </example>

            <example id="zend.db.adapter.connecting.parameters.example2">
                <title>Przekazanie do fabryki opcji automatycznego umieszczania
                w cudzysłowie</title>

                <para>
                    Tą opcję można podać za pomocą stałej
                    <constant>Zend_Db::AUTO_QUOTE_IDENTIFIERS</constant>.
                    Jeśli jej wartość wynosi <constant>TRUE</constant> (domyślnie) to identyfikatory
                    takie jak nazwy tabel, kolumn oraz aliasy w składni każdego    polecenia 
                    <acronym>SQL</acronym> generowanego za pomocą danego adaptera będą 
                    umieszczane w cudzysłowie. Takie podejście upraszcza używanie identyfikatorów
                    zawierających słowa kluczowe <acronym>SQL</acronym> lub znaki specjalne. Jeśli
                    wartość opcji wynosi <constant>FALSE</constant> to identyfikatory nie są
                    umieszczane w cudzysłowie. Jeśli zachodzi potrzeba owinięcia identyfikatorów
                    cudzysłowami należy to zrobić samodzielnie za pomocą metody
                    <methodname>quoteIdentifier()</methodname>.
                </para>

                <programlisting language="php"><![CDATA[
$options = array(
    Zend_Db::AUTO_QUOTE_IDENTIFIERS => false
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'options'        => $options
);

$db = Zend_Db::factory('Pdo_Mysql', $params);
]]></programlisting>
            </example>

            <example id="zend.db.adapter.connecting.parameters.example3">
                <title>Przekazanie do fabryki opcji sterownika PDO</title>

                <programlisting language="php"><![CDATA[
$pdoParams = array(
    PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'driver_options' => $pdoParams
);

$db = Zend_Db::factory('Pdo_Mysql', $params);

echo $db->getConnection()
        ->getAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY);
]]></programlisting>
            </example>

            <example id="zend.db.adapter.connecting.parameters.example4">
                <title>Przekazanie do fabryki opcji serializacji</title>

                <programlisting language="php"><![CDATA[
$options = array(
    Zend_Db::ALLOW_SERIALIZATION => false
);

$params = array(
    'host'           => '127.0.0.1',
    'username'       => 'webuser',
    'password'       => 'xxxxxxxx',
    'dbname'         => 'test',
    'options'        => $options
);

$db = Zend_Db::factory('Pdo_Mysql', $params);
]]></programlisting>
            </example>
        </sect3>

        <sect3 id="zend.db.adapter.connecting.getconnection">
            <title>Zarządzanie leniwymi połączeniami</title>

            <para>
                Utworzenie instancji klasy adaptera nie powoduje natychmiastowego połączenia
                z serwerem bazy danych. Adapter zachowuje parametry połączenia ale łączy się
                na żądanie - w momencie pierwszego wywołania zapytania. Dzięki temu zainicjowanie
                adaptera jest szybkie i tanie. Można utworzyć adapter nawet jeśli nie jest
                się pewnym czy wykonanie jakiegokolwiek zapytania przy danym połączeniu będzie
                niezbędne.
            </para>

            <para>
                Jeśli zajdzie potrzeba zmuszenia adaptera do połączenia z bazą danych,
                należy wówczas wywołać metodę <methodname>getConnection()</methodname>.
                Zwraca ona obiekt połączenia odpowiednio do rozszerzenia
                <acronym>PHP</acronym> używanego do połączenia. Jeśli
                używa się adaptera korzystającego z <acronym>PDO</acronym>, to metoda
                <methodname>getConnection()</methodname>, po zainicjowaniu połączenia
                z serwerem bazy danych, zwróci obiekt <acronym>PDO</acronym>.
            </para>

            <para>
                Możliwość wymuszenia połączenia z bazą danych może być przydatna gdy chce się
                złapać wyjątki rzucane przez adapter powstałe w rezultacie podania złych danych
                uwierzytelniających lub innych błędów połączenia. Te wyjątki nie są rzucane dopóki
                nie jest ustanowiona próba połączenia, więc można uprościć kod aplikacji i obsłużyć
                je w jednym miejscu. W przeciwnym razie należałoby je przechwycać w momencie 
                wywołania pierwszego zapytania do bazy danych.
            </para>

            <para>
                Dodatkowo adapter może zostać poddany serializacji i przechowany
                np. w zmiennej sesyjnej.
                Może to być pomocne nie tylko dla adaptera ale również z punktu widzenia obiektów,
                które z niego korzystają, takich jak <classname>Zend_Db_Select</classname>.
                Domyślnie serializacja adapterów jest dozwolona ale jeśli jest taka
                potrzeba - można ją wyłączyć poprzez podanie opcji
                <constant>Zend_Db::ALLOW_SERIALIZATION</constant> z wartością 
                <constant>FALSE</constant> (przykład niżej).
                Aby pozostać w zgodzie z zasadą leniwego połączenia, adapter nie połączy się
                automatycznie po odserializowaniu. Należy zatem
                wywołać metodę <methodname>getConnection()</methodname>.
                Można również zmusić adapter aby po odserializowaniu łączył się z bazą
                danych automatycznie poprzez podanie opcji
                <constant>Zend_Db::AUTO_RECONNECT_ON_UNSERIALIZE</constant>
                z wartością <constant>TRUE</constant>.
            </para>

            <example id="zend.db.adapter.connecting.getconnection.example">
                <title>Obsługa wyjątków połączenia</title>

                <programlisting language="php"><![CDATA[
try {
    $db = Zend_Db::factory('Pdo_Mysql', $parameters);
    $db->getConnection();
} catch (Zend_Db_Adapter_Exception $e) {
    // przyczyną problemów mogą być złe dane uwierzytelniające lub np. baza danych
    // nie jest uruchomiona
} catch (Zend_Exception $e) {
    // przyczyną może być np. problem z załadowaniem odpowiedniej klasy adaptera
}
]]></programlisting>
            </example>

        </sect3>

    </sect2>

    <sect2 id="zend.db.adapter.example-database">

        <title>Przykładowa baza danych</title>

        <para>
            W dokumentacji klas <classname>Zend_Db</classname> używany jest prosty zestaw tabel w
            celu zilustrowania użycia klas i metod. Te tabele mogą przechowywać dane związane z
            przechowywaniem błędów (bugs) powstałych podczas rozwijania projektu informatycznego.
            Baza danych zawiera cztery tabele:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <emphasis>accounts</emphasis> przechowuje informacje o każdym użytkowniku
                    bazy danych błędów.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>products</emphasis> przechowuje informacje o każdym produkcie,
                    dla którego można zapisać wystąpienie usterki.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>bugs</emphasis> przechowuje informacje o błędach, włączając
                    jego obecny stan, osobę zgłaszającą, osobę przypisaną do rozwiązania
                    problemu oraz osobę przeznaczoną do zweryfikowania poprawności 
                    zastosowanego rozwiązania.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>bugs_products</emphasis> przechowuje relację pomiędzy usterkami
                    a produktami. To odzwierciedla połączenie wiele-do-wielu, ponieważ dany błąd
                    może się odnosić do wielu produktów a jeden produkt może posiadać wiele usterek.
                </para>
            </listitem>
        </itemizedlist>

        <para>
            Poniższy pseudokod definicji danych <acronym>SQL</acronym> opisuje tabele z tego
            przykładu. Te tabele są bardzo często używane w testach jednostkowych
            <classname>Zend_Db</classname>.
        </para>

        <programlisting language="sql"><![CDATA[
CREATE TABLE accounts (
  account_name      VARCHAR(100) NOT NULL PRIMARY KEY
);

CREATE TABLE products (
  product_id        INTEGER NOT NULL PRIMARY KEY,
  product_name      VARCHAR(100)
);

CREATE TABLE bugs (
  bug_id            INTEGER NOT NULL PRIMARY KEY,
  bug_description   VARCHAR(100),
  bug_status        VARCHAR(20),
  reported_by       VARCHAR(100) REFERENCES accounts(account_name),
  assigned_to       VARCHAR(100) REFERENCES accounts(account_name),
  verified_by       VARCHAR(100) REFERENCES accounts(account_name)
);

CREATE TABLE bugs_products (
  bug_id            INTEGER NOT NULL REFERENCES bugs,
  product_id        INTEGER NOT NULL REFERENCES products,
  PRIMARY KEY       (bug_id, product_id)
);
]]></programlisting>

        <para>
            Należy zwrócić uwagę, iż tabela 'bugs' zawiera wiele kluczy obcych
            odnoszących się do tabeli 'accounts'. Każdy z nich może prowadzić do
            innego wiersza tabeli 'accounts' w ramach jednego bugu.
        </para>

        <para>
            Poniższy diagram ilustruje fizyczny model danych przykładowej bazy danych
        </para>

        <para>
            <inlinegraphic width="387" scale="100" align="center" valign="middle"
                fileref="figures/zend.db.adapter.example-database.png" format="PNG" />
        </para>

    </sect2>

    <sect2 id="zend.db.adapter.select">

        <title>Pobranie rezultatów zapytania</title>

        <para>
            Ta część opisuje metody klasy adaptera za pomocą których można wywołać
            zapytania <acronym>SELECT</acronym> oraz pobrać ich rezultaty.
        </para>

        <sect3 id="zend.db.adapter.select.fetchall">

            <title>Pobranie całego zbioru rezultatów</title>

            <para>
                Za pomocą metody <methodname>fetchAll()</methodname> można wywołać zapytanie 
                <acronym>SQL</acronym> <acronym>SELECT</acronym> oraz pobrać jego rezultaty
                w jednym kroku.
            </para>

            <para>
                Pierwszym argumentem metody jest łańcuch znaków zawierający polecenie 
                <acronym>SELECT</acronym>. Alternatywnie, w pierwszym argumencie można umieścić
                obiekt klasy <link linkend="zend.db.select">Zend_Db_Select</link>. Adapter
                automatycznie dokonuje konwersji tego obiektu do łańcucha znaków zawierającego
                zapytanie <acronym>SELECT</acronym>.
            </para>

            <para>
                Drugi argument metody <methodname>fetchAll()</methodname> to tablica wartości
                używanych do zastąpienia parametrów wiązanych w zapytaniu <acronym>SQL</acronym>.
            </para>

            <example id="zend.db.adapter.select.fetchall.example">
                <title>Użycie metody fetchAll()</title>
                <programlisting language="php"><![CDATA[
$sql = 'SELECT * FROM bugs WHERE bug_id = ?';

$result = $db->fetchAll($sql, 2);
]]></programlisting>
            </example>

        </sect3>

        <sect3 id="zend.db.adapter.select.fetch-mode">

            <title>Zmiana trybu zwracania danych</title>

            <para>
                Domyślnie <methodname>fetchAll()</methodname> zwraca tablicę wierszy, z których
                każdy jest tablicą asocjacyjną. Kluczami tablicy asocjacyjnej są kolumny lub ich 
                aliasy podane w zapytaniu <acronym>SELECT</acronym>.
            </para>

            <para>
                Można ustawić inny tryb zwracania rezultatów poprzez metodę
                <methodname>setFetchMode()</methodname>. Dopuszczalne tryby są identyfikowane
                przez stałe:
            </para>

            <itemizedlist>
                <listitem>
                    <para>
                        <emphasis>Zend_Db::FETCH_ASSOC</emphasis>:
                        zwraca dane w postaci tablicy tablic asocjacyjnych.
                        Klucze tablicy asocjacyjnej
                        to nazwy kolumn. To jest domyślny tryb zwrotu danych w klasach
                         <classname>Zend_Db_Adapter</classname>.
                    </para>
                    <para>
                        Należy zwrócić uwagę na fakt iż jeśli lista kolumn do zwrotu
                        zawiera więcej niż     jedną kolumnę o określonej nazwie,
                        np. jeśli pochodzą one z różnych tabel 
                        połączonych klauzulą <acronym>JOIN</acronym>, to w asocjacyjnej
                        tablicy wynikowej może być tylko jeden klucz o podanej nazwie.
                        Jeśli używany jest tryb <acronym>FETCH_ASSOC</acronym>,
                        należy upewnić się, że kolumny w zapytaniu
                        <acronym>SELECT</acronym> posiadają aliasy, dzięki czemu rezultat zapytania
                        będzie zawierał unikatowe nazwy kolumn.
                    </para>
                    <para>
                        Domyślnie, łańcuchy znaków z nazwami kolumn są zwracane w taki sposób w jaki
                        zostały otrzymane przez sterownik bazy danych. Przeważnie jest odpowiada to
                        stylowi nazw kolumn używanego rodzaju bazy danych. Dzięki opcji
                        <constant>Zend_Db::CASE_FOLDING</constant> można określić wielkość
                        zwracanych znaków.
                        Opcji tej można użyć podczas inicjowania adaptera.
                        Przykład: <xref linkend="zend.db.adapter.connecting.parameters.example1" />.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <emphasis>Zend_Db::FETCH_NUM</emphasis>:
                        zwraca dane jako tablicę tablic. Indeksami tablicy są liczby całkowite 
                        odpowiadające pozycji danej kolumny w liście <acronym>SELECT</acronym>
                        zapytania.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <emphasis>Zend_Db::FETCH_BOTH</emphasis>:
                        zwraca dane jako tablicę tablic. Kluczami tablicy są zarówno łańcuchy znaków
                        (tak jak w trybie FETCH_ASSOC) oraz liczby całkowite (tak jak w trybie
                        FETCH_NUM). Należy zwrócić uwagę na fakt iż liczba elementów
                        tablicy wynikowej będzie dwukrotnie większa niż w przypadku
                        użycia trybów  FETCH_ASSOC lub FETCH_NUM.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <emphasis>Zend_Db::FETCH_COLUMN</emphasis>:
                        zwraca dane jako tablicę wartości. Wartości odpowiadają rezultatom
                        zapytania przypisanym jednej kolumnie zbioru wynikowego.
                        Domyślnie, jest to pierwsza kolumna,
                        indeksy rozpoczynają się od 0.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <emphasis>Zend_Db::FETCH_OBJ</emphasis>:
                        zwraca dane jako tablicę obiektów. Domyślną klasą jest wbudowana
                        w <acronym>PHP</acronym> klasa stdClass.
                        Kolumny rezultatu zapytania stają się właściwościami powstałego obiektu.
                    </para>
                </listitem>
            </itemizedlist>

            <example id="zend.db.adapter.select.fetch-mode.example">
                <title>Użycie metody setFetchMode()</title>
                <programlisting language="php"><![CDATA[
$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchAll('SELECT * FROM bugs WHERE bug_id = ?', 2);

// $result jest tablicą obiektów 
echo $result[0]->bug_description;
]]></programlisting>
            </example>

        </sect3>

        <sect3 id="zend.db.adapter.select.fetchassoc">

            <title>Pobranie rezultatów jako tablicy asocjacyjnej</title>

            <para>
                Metoda <methodname>fetchAssoc()</methodname> zwraca dane w formie tablicy tablic
                asocjacyjnych, niezależnie od wartości ustalonej jako tryb
                zwracania rezultatów zapytania.
            </para>

            <example id="zend.db.adapter.select.fetchassoc.example">
                <title>Użycie fetchAssoc()</title>
                <programlisting language="php"><![CDATA[
$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchAssoc('SELECT * FROM bugs WHERE bug_id = ?', 2);

// $result staje się tablicą tablic asocjacyjnych, mimo ustawionego
// trybu zwracania rezultatów zapytania
echo $result[0]['bug_description'];
]]></programlisting>
            </example>

        </sect3>

        <sect3 id="zend.db.adapter.select.fetchcol">

            <title>Zwrócenie pojedynczej kolumny ze zbioru wynikowego</title>

            <para>
                Metoda <methodname>fetchCol()</methodname> zwraca dane w postaci tablicy wartości
                niezależnie od wartości ustalonej jako tryb zwrcania rezultatów zapytania. Ta
                metoda zwraca pierwszą kolumnę ze zbioru powstałego na skutek wywołania zapytania.
                Inne kolumny znajdujące się w tym zbiorze są ignorowane. Aby zwrócić inną niż 
                pierwsza kolumnę należy skorzystać z przykładu
                <xref linkend="zend.db.statement.fetching.fetchcolumn" />.
            </para>

            <example id="zend.db.adapter.select.fetchcol.example">
                <title>Użycie fetchCol()</title>
                <programlisting language="php"><![CDATA[
$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchCol(
    'SELECT bug_description, bug_id FROM bugs WHERE bug_id = ?', 2);

// zawiera bug_description; bug_id nie zostanie zwrócona
echo $result[0];
]]></programlisting>
            </example>

        </sect3>

        <sect3 id="zend.db.adapter.select.fetchpairs">

            <title>Zwrócenie ze zbioru wynikowego par klucz-wartość</title>

            <para>
                Metoda <methodname>fetchPairs()</methodname> zwraca dane w postaci tablicy
                par klucz-wartość. Zwracana jest to tablica asocjacyjna z pojedynczą wartością
                w każdym polu. Wartości z pierwszej kolumny zapytania <acronym>SELECT</acronym>
                stają się kluczami tablicy wynikowej zaś wartości drugiej zostają umieszczone
                jako wartości tablicy. Pozostałe kolumny zwracane przez zapytanie są ignorowane.
            </para>

            <para>
                Należy konstruować zapytanie <acronym>SELECT</acronym> w taki sposób aby pierwsza
                kolumna posiadała unikalne wartości. W przeciwnym wypadku wartości
                tablicy asocjacyjnej zostaną nadpisane.
            </para>

            <example id="zend.db.adapter.select.fetchpairs.example">
                <title>Użycie fetchPairs()</title>
                <programlisting language="php"><![CDATA[
$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchPairs('SELECT bug_id, bug_status FROM bugs');

echo $result[2];
]]></programlisting>
            </example>
        </sect3>

        <sect3 id="zend.db.adapter.select.fetchrow">

            <title>Zwrócenie pojedynczego wiersza ze zbioru wynikowego</title>

            <para>
                Metoda <methodname>fetchRow()</methodname> pobiera dane używając bieżącego 
                trybu zwracania rezultatów ale zwraca jedynie pierwszy wiersz ze zbioru
                wynikowego.
            </para>

            <example id="zend.db.adapter.select.fetchrow.example">
                <title>Using fetchRow()</title>
                <programlisting language="php"><![CDATA[
$db->setFetchMode(Zend_Db::FETCH_OBJ);

$result = $db->fetchRow('SELECT * FROM bugs WHERE bug_id = 2');

// $result to pojedynczy obiekt a nie tablica obiektów
echo $result->bug_description;
]]></programlisting>
            </example>
        </sect3>

        <sect3 id="zend.db.adapter.select.fetchone">

            <title>Zwrócenie pojedynczej wartości ze zbioru wynikowego</title>

            <para>
                Metoda <methodname>fetchOne()</methodname> można opisać jako kombinacja 
                <methodname>fetchRow()</methodname> oraz <methodname>fetchCol()</methodname>
                bo zwraca dane pochodzące z pierwszego wiersza zbioru wynikowego ograniczając
                kolumny do pierwszej w wierszu. Ostatecznie zostaje zwrócona pojedyncza
                wartość skalarna a nie tablica czy obiekt
            </para>

            <example id="zend.db.adapter.select.fetchone.example">
                <title>Using fetchOne()</title>
                <programlisting language="php"><![CDATA[
$result = $db->fetchOne('SELECT bug_status FROM bugs WHERE bug_id = 2');

// pojedyncza wartość string
echo $result;
]]></programlisting>
            </example>
        </sect3>

    </sect2>

    <sect2 id="zend.db.adapter.write">

        <title>Zapisywanie zmian do bazy danych</title>

        <para>
            Adaptera klasy można użyć również do zapisania nowych, bądź do zmiany
            istniejących danych w bazie. Ta część opisuje metody włąściwe dla tych operacji.
        </para>

        <sect3 id="zend.db.adapter.write.insert">

            <title>Dodawanie danych</title>

            <para>
                Za pomocą metody <methodname>insert()</methodname> można dodać nowe wiersze
                do tabeli bazy danych. Jej pierwszym argumentem jest łańcuch znaków oznaczający
                nazwę tabeli. Drugim - tablica asocjacyjna z odwzorowaniem nazw kolumn i wartości
                jakie mają zostać w nich zapisane.
            </para>

            <example id="zend.db.adapter.write.insert.example">
                <title>Dodawanie danych do tabeli</title>
                <programlisting language="php"><![CDATA[
$data = array(
    'created_on'      => '2007-03-22',
    'bug_description' => 'Something wrong',
    'bug_status'      => 'NEW'
);

$db->insert('bugs', $data);
]]></programlisting>
            </example>

            <para>
                Kolumny, które nie zostały podane w tablicy z danymi nie będą umieszczone
                w zapytaniu na zasadzie analogicznej do użycia polecenia
                <acronym>SQL</acronym> <acronym>INSERT</acronym>:
                Jeśli kolumna ma podaną wartość domyślną (<acronym>DEFAULT</acronym>)
                to ta wartość zostanie zapisana w nowym wierszu.
                W przeciwnym przypadku kolumna nie będzie miała żadnej 
                wartości (<constant>NULL</constant>).
            </para>

            <para>
                Domyślnym sposobem zapisu danych jest użycie parametrów wiązanych.
                Dzięki temu ryzyko wystąpienia niektórych form zagrożenia
                bezpieczeństwa aplikacji jest ograniczone.
                W tablicy z danymi nie trzeba używać unikania
                lub umieszczania wartości w cudzysłowiu.
            </para>

            <para>
                Może wystąpić potrzeba potraktowania wartości w tablicy danych jako wyrażeń
                <acronym>SQL</acronym>. W takim wypadku nie powinno się stosować
                umieszczania w cudzysłowiu.
                Domyślnie wszystkie podane wartości string są traktowane jak literały.
                Aby upewnić się, że
                wartość jest wyrażeniem <acronym>SQL</acronym> i nie powinna zostać umieszczona w 
                cudzysłowie, należy ją podać jako obiekt klasy
                <classname>Zend_Db_Expr</classname> zamiast
                prostego łańcucha znaków.
            </para>

            <example id="zend.db.adapter.write.insert.example2">
                <title>Umieszczanie wyrażeń w tabeli</title>
                <programlisting language="php"><![CDATA[
$data = array(
    'created_on'      => new Zend_Db_Expr('CURDATE()'),
    'bug_description' => 'Something wrong',
    'bug_status'      => 'NEW'
);

$db->insert('bugs', $data);
]]></programlisting>
            </example>

        </sect3>

        <sect3 id="zend.db.adapter.write.lastinsertid">

            <title>Pobranie wygenerowanej wartości</title>

            <para>
                Niektóre systemy zarządzania bazą danych wspierają klucze pierwotne o automatycznie 
                zwiększanych wartościach. Tabela zdefiniowana w ten sposób generuje wartość klucza
                pierwotnego automatycznie przy każdym poleceniu <acronym>INSERT</acronym>.
                Wartością zwracaną przez metodę
                <methodname>insert()</methodname> <emphasis>nie</emphasis> jest
                ostatni zapisany identyfikator, ponieważ tabela mogła nie posiadać automatycznie 
                inkrementowanej kolumny.
                Zamiast tego wartością zwrotną jest ilość zapisanych wierszy (przeważnie 1). 
            </para>

            <para>
                Jeśli tabela jest zdefiniowana za pomocą automatycznego klucza pierwotnego to można
                użyć metody <methodname>lastInsertId()</methodname>.
                Po udanym dodaniu wiersza metoda
                ta zwraca ostatnią wartość klucza wygenerowaną w ramach bieżącego połączenia.
            </para>

            <example id="zend.db.adapter.write.lastinsertid.example-1">
                <title>Użycie lastInsertId() dla automatycznego klucza pierwotnego</title>
                <programlisting language="php"><![CDATA[
$db->insert('bugs', $data);

// zwrócenie ostatniej wartości wygenerowanej przez automatyczną kolumnę
$id = $db->lastInsertId();
]]></programlisting>
            </example>

            <para>
                Niektóre systemy zarządzania bazą danych wspierają obiekt sekwencji, który służy
                do generowania unikalnych wartości, które mogą posłużyć jako klucz pierwotny.
                Aby obsługiwać również sekwencje metoda <methodname>lastInsertId()</methodname>
                akceptuje dodatkowe dwa argumenty. Pierwszym z nich jest nazwa tabeli zaś 
                drugim - nazwa kolumny. Założona jest konwencja według której sekwencja ma nazwę
                składającą się z nazwy tabeli oraz kolumny do której generuje wartości z dodatkowym
                sufiksem "_seq". Podstawą tego założenia są domyślne ustawienia PostgreSQL.
                Przykładowo tabela "bugs" z kluczem pierwotnym w kolumnie "bug_id"
                będzie używała sekwencji o nazwie "bugs_bug_id_seq".
            </para>

            <example id="zend.db.adapter.write.lastinsertid.example-2">
                <title>Użycie lastInsertId() dla sekwencji</title>
                <programlisting language="php"><![CDATA[
$db->insert('bugs', $data);

// zwrócenie ostatniej wartości wygenerowanej przez sekwencję 'bugs_bug_id_seq'
$id = $db->lastInsertId('bugs', 'bug_id');

// zwrócenie ostatniej wartości wygenerowanej przez sekwencję 'bugs_seq'
$id = $db->lastInsertId('bugs');
]]></programlisting>
            </example>

            <para>
                Jeśli nazwa obiektu sekwencji nie podąża za tą konwencją można użyć metody
                <methodname>lastSequenceId()</methodname>, która przyjmuje pojedynczy łańcuch
                znaków - nazwę sekwencji - jako argument.
            </para>

            <example id="zend.db.adapter.write.lastinsertid.example-3">
                <title>Użycie lastSequenceId()</title>
                <programlisting language="php"><![CDATA[
$db->insert('bugs', $data);

// zwrócenie ostatniej wartości wygenerowanej przez sekwencję 'bugs_id_gen'
$id = $db->lastSequenceId('bugs_id_gen');
]]></programlisting>
            </example>

            <para>
                Dla systemów zarządzania bazą danych które nie wspierają sekwencji, włączając MySQL,
                Microsoft <acronym>SQL</acronym> Server, oraz SQLite dodatkowe argumenty podane do
                metody <methodname>lastInsertId()</methodname> są ignorowane a zwrócona jest
                ostatnia wartość wygenerowana dla dowolnej tabeli poprzez polecenie
                <acronym>INSERT</acronym>
                podczas bieżącego połączenia. Dla tych <acronym>RDBMS</acronym> metoda 
                <methodname>lastSequenceId()</methodname> zawsze zwraca <constant>NULL</constant>.
            </para>

            <note>
                <title>Dlaczego nie należy używać "SELECT MAX(id) FROM table"?</title>
                <para>
                    Powyższe zapytanie często zwraca ostatni klucz pierwotny, jaki został zapisany
                    w wierszu tabeli. Ten sposób nie jest jednak bezpieczny w przypadku, gdy
                    wiele klientów zapisuje rekordy do bazy danych. Jest możliwe, że w momencie 
                    pomiędzy zapisaniem jednego rekordu a zapytaniem o 
                    <methodname>MAX(id)</methodname>, do tabeli zostanie zapisany kolejny rekord, 
                    wprowadzony przez innego użytkownika. Przez to zwrócona wartość nie będzie 
                    identyfikowała pierwszego wprowadzonego wiersza tylko drugi - wprowadzony przez
                    inne połączenie. Nie ma pewnego sposobu na wykrycie, że do takiego zdarzenia
                    doszło.
                </para>
                <para>
                    Zastosowanie mocnej izolacji transakcji, tak jak przy trybie
                    "repeatable read" może pomóc w ograniczeniu ryzyka wystąpienia takiej
                    sytuacji ale niektóre systemy zarządzania bazą danych nie wspierają
                    izolacji transakcji w wystarczającym stopniu lub aplikacja może
                    intencjonalnie korzystać jedynie z niskiego trybu izolacji transakcji.
                </para>

                <para>
                    Dodatkowo, używanie wyrażeń takich jak "MAX(id)+1" do generowania
                    nowych wartości klucza głównego nie jest bezpieczne bo
                    dwa połączenia mogą wykonać to zapytanie 
                    praktycznie jednocześnie i w rezultacie próbować użyć jednakowej wartości 
                    dla kolejnego polecenia <acronym>INSERT</acronym>.
                </para>

                <para>
                    Wszystkie systemy zarządzania bazą danych udostępniają
                    mechanizmy służące generowaniu
                    unikalnych wartości oraz zwracaniu ostatniej z nich.
                    Mechanizmy te intencjonalnie działają poza zasięgiem izolacji
                    transakcji więc nie jest możliwe aby wartość wygenerowana dla jednego
                    klienta pojawiła się jako ostatnia wygenerowana dla innego połączenia.
                </para>
            </note>

        </sect3>

        <sect3 id="zend.db.adapter.write.update">
            <title>Aktualizacja danych</title>

            <para>
                Aktualizacji wierszy tabeli w bazie danych można dokonać poprzez metodę adaptera
                <methodname>update()</methodname> . Metoda przyjmuje trzy argumenty: pierwszym 
                jest nazwa tabeli; drugim - tablica asocjacyjna mapująca kolumny, które mają ulec
                zmianie oraz wartości jakie kolumny przybiorą; trzecim - warunek
                wyznaczający wiersze do zmiany.
            </para>

            <para>
                Wartości w tablicy z danymi są traktowane jak literały znakowe. Należy zapoznać
                się z akapitem <xref linkend="zend.db.adapter.write.insert" /> aby uzyskać więcej
                informacji na temat użyci w niej wyrażeń <acronym>SQL</acronym>. 
            </para>

            <para>
                Trzecim argumentem metody jest string zawierający wyrażenie <acronym>SQL</acronym>
                będące kryterium wyznaczającym wiersze do zmiany. Wartości i identyfikatory w tym
                argumencie nie są umieszczane w cudzysłowie ani unikane. Należy, więc, zadbać o to
                samemu. Należy zapoznać się z akapitem <xref linkend="zend.db.adapter.quoting" />
                aby zasięgnąć dodatkowych informacji.
            </para>

            <para>
                Wartością zwracaną jest ilość wierszy zmienionych przez operację aktualizacji.
            </para>

            <example id="zend.db.adapter.write.update.example">
                <title>Aktualizacja wierszy</title>
                <programlisting language="php"><![CDATA[
$data = array(
    'updated_on'      => '2007-03-23',
    'bug_status'      => 'FIXED'
);

$n = $db->update('bugs', $data, 'bug_id = 2');
]]></programlisting>
            </example>

            <para>
                Jeśli trzeci argument nie zostanie podany to wszystkie wiersze w tabeli
                zostaną zaktualizowane zgodnie z kolumnami i wartościami podanymi w 
                drugim argumencie.
            </para>

            <para>
                Jeśli trzeci argument zostanie podany jako tablica łańcuchów znakowych to
                w zapytaniu łańcuchy zostaną połączone za pomocą operatorów 
                <constant>AND</constant>.
            </para>

            <para>
                Przy podaniu trzeciego argumentu jako tablicy tablic, ich wartości
                zostaną automatycznie
                otoczone cudzysłowami i umieszczone w kluczach tych tablic. Po czym zostaną
                połączone za pomocą operatorów <constant>AND</constant>.
            </para>

            <example id="zend.db.adapter.write.update.example-array">
                <title>Aktualizacja wierszy tablicy za pomocą tablicy wyrażeń</title>
                <programlisting language="php"><![CDATA[
$data = array(
    'updated_on'      => '2007-03-23',
    'bug_status'      => 'FIXED'
);

$where[] = "reported_by = 'goofy'";
$where[] = "bug_status = 'OPEN'";

$n = $db->update('bugs', $data, $where);

// Powstałe wyrażenie SQL to:
//  UPDATE "bugs" SET "update_on" = '2007-03-23', "bug_status" = 'FIXED'
//  WHERE ("reported_by" = 'goofy') AND ("bug_status" = 'OPEN')
]]></programlisting>
            </example>

            <example id="zend.db.adapter.write.update.example-arrayofarrays">
                <title>Aktualizacja wierszy tablicy za pomocą tablicy tablic</title>
                <programlisting language="php"><![CDATA[
$data = array(
    'updated_on'      => '2007-03-23',
    'bug_status'      => 'FIXED'
);

$where['reported_by = ?'] = 'goofy';
$where['bug_status = ?']  = 'OPEN';

$n = $db->update('bugs', $data, $where);

// Powstałe wyrażenie SQL to:
//  UPDATE "bugs" SET "update_on" = '2007-03-23', "bug_status" = 'FIXED'
//  WHERE ("reported_by" = 'goofy') AND ("bug_status" = 'OPEN')
]]></programlisting>
            </example>
        </sect3>

        <sect3 id="zend.db.adapter.write.delete">
            <title>Usuwanie danych</title>
            <para>
                Usuwania danych można dokonać używając metody <methodname>delete()</methodname>.
                Przyjmuje ona dwa argumenty: pierwszym z nich jest łańcuch znaków z nazwą tabeli; 
                Drugim - warunek określający wiersze do usunięcia.
            </para>

            <para>
                Drugi argument to string zawierający wyrażenie <acronym>SQL</acronym> użyte jako
                kryterium wyznaczania usuwanych wierszy. Wartości i identyfikatory nie są
                unikane ani umieszczane w cudzysłowie - należy zatroszczyć się o to samemu. Aby
                dowiedzieć się więcej na ten temat
                można skorzystać z akapitu <xref linkend="zend.db.adapter.quoting" />.
            </para>

            <para>
                Wartość zwrotna to ilość wierszy, jakie uległy zmianie w wyniku zadziałania
                operacji usuwania.
            </para>

            <example id="zend.db.adapter.write.delete.example">
                <title>Usuwanie wierszy</title>
                <programlisting language="php"><![CDATA[
$n = $db->delete('bugs', 'bug_id = 3');
]]></programlisting>
            </example>

            <para>
                Jeśli drugi argument nie zostanie podany to wszystkie wiersze z tabeli
                ulegną usunięciu.
            </para>

            <para>
                Jeśli drugi argument zostanie podany jako tablica łańcuchów znaków to
                te łańcuchy ulegną konkatenacji jako wyrażenia logiczne połączone
                operatorem <constant>AND</constant>.
            </para>

            <para>
                Jeśli trzeci argument będzie tablicą tablic to jej wartości zostaną
                automatycznie otoczone cudzysłowami i umieszczona w kluczach tablic. Potem
                zostaną połączone razem za pomocą operatora <constant>AND</constant>.
            </para>

        </sect3>

    </sect2>

    <sect2 id="zend.db.adapter.quoting">

        <title>Umieszczanie wartości i identyfikatorów w cudzysłowie</title>

        <para>
            Podczas tworzenia zapytań <acronym>SQL</acronym> często dochodzi do sytuacji, w której
            niezbędne jest umieszczenie wartości zmiennych <acronym>PHP</acronym> w wyrażeniach
            <acronym>SQL</acronym>. Stanowi to zagrożenie, ponieważ jeśli wartość
            łańcuchowej zmiennej <acronym>PHP</acronym> zawiera określone znaki, takie jak symbol
            cudzysłowu, to mogą one spowodować błąd w poleceniu <acronym>SQL</acronym>. Poniżej
            znajduje się przykład zapytania zawierającego niespójną liczbę symboli cudzysłowu:
            <programlisting language="php"><![CDATA[
$name = "O'Reilly";
$sql = "SELECT * FROM bugs WHERE reported_by = '$name'";

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O'Reilly'
]]></programlisting>
        </para>

        <para>
            
            
            Zagrożenie potęguje fakt iż podobne błędy mogą zostać wykorzystane przez osobę
            próbującą zmanipulować działanie funkcji aplikacji. Jeśli takiej osobie
            uda się wpłynąć na wartość zmiennej <acronym>PHP</acronym> poprzez
            parametr <acronym>HTTP</acronym>
            (bądź inny mechanizm), to będzie mogła zmienić zapytanie <acronym>SQL</acronym>
            tak by dokonywało operacji zupełnie niezamierzonych przez twórców, takich jak
            zwrócenie danych, do których osoba nie powinna mieć dostępu. To jest poważny i szeroko
            rozpowszechniony sposób łamania zabezpieczeń aplikacji znany pod nazwą "SQL Injection"
            (wstrzyknięcie SQL - <ulink url="http://en.wikipedia.org/wiki/SQL_Injection">
            http://en.wikipedia.org/wiki/SQL_Injection</ulink>).
        </para>

        <para>
            Adaptery klasy <classname>Zend_Db</classname> udostępniają funkcje przydatne
            do zredukowania zagrożenia atakiem <acronym>SQL</acronym> Injection w kodzie
            <acronym>PHP</acronym>.
            Przyjętym rozwiązaniem jest unikanie znaków specjalnych takich jak cudzysłów w 
            wartościach zmiennych <acronym>PHP</acronym>
            przed umieszczeniem ich w poleceniu <acronym>SQL</acronym>.
            Takie podejście zapewnia ochronę przed przypadkową jak i intencjonalną zmianą
            znaczenia poleceń <acronym>SQL</acronym> przez
            zmienne <acronym>PHP</acronym> zawierające znaki specjalne.
        </para>

        <sect3 id="zend.db.adapter.quoting.quote">

            <title>Użycie quote()</title>

            <para>
                Metoda <methodname>quote()</methodname> przyjmuje pojedynczy
                argument - łańcuch znaków.
                Na wyjściu zwraca podany argument ze wszystkimi znakami specjalnymi
                poddanymi uniknięciu w sposób właściwy dla używanego
                <acronym>RDBMS</acronym> i otoczony znakami ograniczającymi
                łańcuchy znakowe. Standardowym znakiem ograniczającym
                stringi w <acronym>SQL</acronym> jest pojedynczy cudzysłów (').
            </para>

            <example id="zend.db.adapter.quoting.quote.example">
                <title>Użycie quote()</title>
                <programlisting language="php"><![CDATA[
$name = $db->quote("O'Reilly");
echo $name;
// 'O\'Reilly'

$sql = "SELECT * FROM bugs WHERE reported_by = $name";

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O\'Reilly'
]]></programlisting>
            </example>

            <para>
                Należy zwrócić uwagę, że wartość zwrotna metody <methodname>quote()</methodname>
                zawiera znaki ograniczające stringi. Jest to zachowanie różne od niektórych funkcji
                które unikają specjalne znaki ale nie ujmują całego łańcucha w znaki ograniczające,
                tak jak <ulink url="http://www.php.net/mysqli_real_escape_string">
                mysql_real_escape_string()</ulink>.
            </para>

            <para>
                Wartości mogą wymagać umieszczenia w cudzysłowach lub nie, zgodnie z
                kontekstem używanego typu danych <acronym>SQL</acronym>.
                W przypadku niektórych rodzajów systemów zarządzania
                bazą danych liczby całkowite nie mogą być ograniczane cudzysłowami
                jeśli są porównywane z kolumną lub wyrażeniem zwracającym inną liczbę całkowitą.
                Innymi słowy, poniższy zapis może wywołać błąd w niektórych
                implementacjach <acronym>SQL</acronym> zakładając, że
                <property>intColumn</property> jest określona
                typem danych <constant>INTEGER</constant>.

                <programlisting language="php"><![CDATA[
SELECT * FROM atable WHERE intColumn = '123'
]]></programlisting>
            </para>

            <para>
                Można użyć opcjonalnego, drugiego argumentu metody <methodname>quote()</methodname>
                aby określić typ danych <acronym>SQL</acronym> i ograniczyć stosowanie cudzysłowu.
            </para>

            <example id="zend.db.adapter.quoting.quote.example-2">
                <title>Użycie quote() z podanym typem danych SQL</title>

                <programlisting language="php"><![CDATA[
$value = '1234';
$sql = 'SELECT * FROM atable WHERE intColumn = '
     . $db->quote($value, 'INTEGER');
]]></programlisting>
            </example>

            <para>
                Każda klasa <classname>Zend_Db_Adapter</classname> ma zapisane nazwy numerycznych
                typów danych <acronym>SQL</acronym> odpowiednio do swojego rodzaju
                <acronym>RDBMS</acronym>.
                Zamiast tego można w tym miejscu również korzystać ze stałych  
                <constant>Zend_Db::INT_TYPE</constant>,
                <constant>Zend_Db::BIGINT_TYPE</constant> oraz
                <constant>Zend_Db::FLOAT_TYPE</constant> aby uniezależnić kod
                od rodzaju bazy danych.
            </para>

            <para>
                <classname>Zend_Db_Table</classname> automatycznie określa
                typy danych <acronym>SQL</acronym>
                dla metody <methodname>quote()</methodname> podczas generowania
                zapytań <acronym>SQL</acronym> odnoszących się do klucza pierwotnego tabeli.
            </para>

        </sect3>

        <sect3 id="zend.db.adapter.quoting.quote-into">

            <title>Użycie quoteInto()</title>

            <para>
                Najbardziej typowym przypadkiem użycia cudzysłowu do ograniczania zmiennych jest
                umieszczenie zmiennej <acronym>PHP</acronym> w zapytaniu <acronym>SQL</acronym>.
                Aby to osiągnąć można użyć metody <methodname>quoteInto()</methodname>.
                Przyjmuje ona dwa argumenty: pierwszym jest łańcuch znaków zawierający
                symbol "?", który zostanie zastąpiony; drugim jest zmienna
                <acronym>PHP</acronym>, która ma trafić na miejsce "?".
            </para>

            <para>
                Zastępowany symbol jest używany przez wielu producentów baz danych do oznaczenia
                pozycyjnych parametrów wiązanych ale metoda <methodname>quoteInto()</methodname>
                jedynie emuluje takie parametry. W wyniku jej działania wartość zmiennej jest
                zwyczajnie umieszczana w łańcuchu z zapytaniem, po wstępnym uniknięciu specjalnych
                znaków i umieszczeniu w cudzysłowie. Prawdziwa implementacja parametrów wiązanych
                zakłada separację łańcucha znaków polecenia <acronym>SQL</acronym> od samych
                parametrów oraz wstępne parsowanie polecenia na serwerze bazy danych.
            </para>

            <example id="zend.db.adapter.quoting.quote-into.example">
                <title>Użycie quoteInto()</title>
                <programlisting language="php"><![CDATA[
$sql = $db->quoteInto("SELECT * FROM bugs WHERE reported_by = ?", "O'Reilly");

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 'O\'Reilly'
]]></programlisting>
            </example>

            <para>
                Można użyć opcjonalnego, trzeciego parametru metody
                <methodname>quoteInto()</methodname>
                aby określić typ danych <acronym>SQL</acronym>.
                Numeryczne typy danych (w przeciwieństwie
                do innych typów) nie podlegają umieszczaniu w cudzysłowie.
            </para>

            <example id="zend.db.adapter.quoting.quote-into.example-2">
                <title>Użycie quoteInto() z podaniem typu danych SQL</title>
                <programlisting language="php"><![CDATA[
$sql = $db
    ->quoteInto("SELECT * FROM bugs WHERE bug_id = ?", '1234', 'INTEGER');

echo $sql;
// SELECT * FROM bugs WHERE reported_by = 1234
]]></programlisting>
            </example>

        </sect3>
        <sect3 id="zend.db.adapter.quoting.quote-identifier">

            <title>Użycie quoteIdentifier()</title>

            <para>
                Wartości nie są jedyną częścią składni polecenia <acronym>SQL</acronym>, która może
                być zmienna. W przypadku użycia zmiennych <acronym>PHP</acronym> do określenia
                tabel, kolumn lub innych identyfikatorów zapytania <acronym>SQL</acronym>,
                może zajść potrzeba umieszczenia również tych elementów w cudzysłowie.
                Domyślnie identyfikatory <acronym>SQL</acronym> muszą przestrzegać określonych
                reguł - podobnie jak w <acronym>PHP</acronym> oraz większości innych
                języków programowania. Przykładem takiej reguły jest zakaz używania spacji
                lub określonych znaków interpunkcyjnych, specjalnych ani też znaków spoza ASCII.
                Poza tym istnieje lista określonych słów, które służą
                do tworzenia zapytań - ich też nie powinno się używać jako identyfikatorów.
            </para>

            <para>
                Mimo to <acronym>SQL</acronym> posiada
                <emphasis>ograniczone identyfikatory</emphasis> (delimited identifiers),
                dzięki którym można skorzystać z większego wachlarza znaków
                do nazywania obiektów.
                Jeśli określony identyfikator zostanie ograniczony odpowiednim
                rodzajem cudzysłowu to będzie można użyć znaków, które bez tego ograniczenia 
                spowodowałyby błąd. Identyfikatory ograniczone mogą zawierać spacje,
                znaki interpunkcyjne czy lokalne litery.
                Można nawet używać zarezerwowanych słów <acronym>SQL</acronym> jeśli
                zostaną otoczone odpowiednimi znakami ograniczającymi.
            </para>

            <para>
                Metoda <methodname>quoteIdentifier()</methodname> zachowuje się podobnie jak
                <methodname>quote()</methodname> ale otacza podany łańcuch znaków za pomocą
                znaków ograniczających zgodnie z rodzajem używanego adaptera.
                Standardowy <acronym>SQL</acronym>
                używa podwójnego cudzysłowu (") jako znaku ograniczającego a większość systemów
                zarządzania bazą danych podąża za tym przykładem. MySQL domyślnie używa znaków 
                back-tick (`). Metoda <methodname>quoteIdentifier()</methodname> dokonuje również
                unikania znaków specjalnych znajdujących się w przekazanym argumencie.
            </para>

            <example id="zend.db.adapter.quoting.quote-identifier.example">
                <title>Użycie quoteIdentifier()</title>
                <programlisting language="php"><![CDATA[
// można użyć tabeli o nazwie takiej samej jak słowo zarezerwowane SQL
$tableName = $db->quoteIdentifier("order");

$sql = "SELECT * FROM $tableName";

echo $sql
// SELECT * FROM "order"
]]></programlisting>
            </example>

            <para>
                W <acronym>SQL</acronym> identyfikatory ograniczone są wrażliwe na wielkość liter,
                w przeciwieństwie do zwykłych identyfikatorów. Przez to, należy upewnić się, że
                pisownia identyfikatora w 100% odpowiada pisowni zapisanej w schemacie, włącznie
                z wielkością liter.
            </para>

            <para>
                W większości przypadków gdzie polecenie <acronym>SQL</acronym> jest
                generowane wewnątrz klas <classname>Zend_Db</classname>,
                domyślnym zachowaniem jest automatyczne ograniczanie 
                identyfikatorów. Można to zmienić poprzez opcję
                <constant>Zend_Db::AUTO_QUOTE_IDENTIFIERS</constant> wywoływaną
                podczas inicjalizacji adaptera.
                Więcej informacji:
                <xref linkend="zend.db.adapter.connecting.parameters.example2" />.
            </para>

        </sect3>

    </sect2>

    <sect2 id="zend.db.adapter.transactions">

        <title>Kontrolowanie transakcji bazy danych</title>

        <para>
            W świecie baz danych transakcja to zbiór operacji, który może zostać zapisany
            bądź cofnięty za pomocą jednej instrukcji, nawet jeśli zmiany wynikające z tych operacji
            dotyczyły wielu tabel. Wszystkie zapytania do bazy danych są przeprowadzane w 
            kontekście transakcji. Jeśli nie są zarządzane jawnie to sterownik bazy danych używa ich
            w sposób przezroczysty dla użytkownika. Takie podejście nazywa się trybem 
            <emphasis>auto-commit</emphasis> - sterownik bazy danych tworzy transakcje dla każdego
            tworzonego polecenia i zapisuje efekty jego działania po każdym wywołaniu polecenia 
            <acronym>SQL</acronym>. Domyślnie wszystkie adaptery <classname>Zend_Db</classname>
            działają w trybie <emphasis>auto-commit</emphasis>.
        </para>

        <para>
            Można również bezpośrednio wskazać początek i koniec transakcji i w ten sposób 
            kontrolować ilość zapytań <acronym>SQL</acronym> jakie trafiają do zapisania (bądź 
            cofnięcia) jako pojedyncza operacja. Aby rozpocząć transakcję należy wywołać metodę
            <methodname>beginTransaction()</methodname>. Następujące po niej polecenia
            <acronym>SQL</acronym> są wykonywane w kontekście wspólnej transakcji do momentu
            zasygnalizowania jej końca.
        </para>

        <para>
            Aby zakończyć transakcję należy użyć metody <methodname>commit()</methodname> lub
            <methodname>rollBack()</methodname>.
            Pierwsza z nich powoduje zapisanie zmian wynikających
            z operacji przeprowadzonych w czasie transakcji.
            Oznacza to, że efekty tych zmian będą
            widoczne w wynikach zapytań wywołanych w ramach innych transakcji.
        </para>

        <para>
            Metoda <methodname>rollBack()</methodname> dokonuje odwrotnej operacji: cofa zmiany
            dokonane podczas transakcji.
            W efekcie wszystkie dane zmienione podczas transakcji zostają cofnięte do
            wartości z momentu przed jej rozpoczęciem. Cofnięcie zmian jednej transakcji nie
            ma wpływu na zmiany dokonane przez inną transakcję trwającą nawet w tym samym czasie.
        </para>

        <para>
            Po skończonej transakcji <classname>Zend_Db_Adapter</classname> wraca
            do trybu auto-commit do momentu ponownego wywołania metody
            <methodname>beginTransaction()</methodname> i ręcznego
            rozpoczęcia nowej transakcji.
        </para>

        <example id="zend.db.adapter.transactions.example">
            <title>Kontrolowanie transakcjami dla zachowania spójności</title>
            <programlisting language="php"><![CDATA[
// Ręczne rozpoczęcie transakcji
$db->beginTransaction();

try {
    // Próba wywołania jednego bądź wielu zapytań:
    $db->query(...);
    $db->query(...);
    $db->query(...);

    // Jeśli wszystkie odniosły sukces - zapisanie transakcji, dzięki czemu wszystkie rezultaty
    // zostaną zapisane za jednym razem.
    $db->commit();

} catch (Exception $e) {
    // Jeśli któreś z zapytań zakończyło się niepowodzeniem i został wyrzucony wyjątek, należy
    // cofnąć całą transakcję odwracając zmiany w niej dokonane (nawet te które zakończyły się
    // sukcesem). Przez to albo wszystkie zmiany zostają zapisane albo żadna.
    $db->rollBack();
    echo $e->getMessage();
}
]]></programlisting>
        </example>

    </sect2>

    <sect2 id="zend.db.adapter.list-describe">

        <title>Uzyskiwanie listy i opisu tabel</title>

        <para>
            Metoda <methodname>listTables()</methodname> zwraca tablicę łańcuchów znakowych,
            zawierającą wszystkie tabele w bieżącej bazie danych.
        </para>

        <para>
            Metoda <methodname>describeTable()</methodname> zwraca tablicę asocjacyjną metadanych
            tabeli. Jako argument należy podać nazwę tabeli.
            Drugi argument jest opcjonalny - wskazuje
            nazwę schematu w którym tabela się znajduje.
        </para>

        <para>
            Kluczami zwracanej tablicy asocjacyjnej są nazwy kolumn tabeli. Wartość przy każdej 
            kolumnie to następna tablica asocjacyjna z następującymi kluczami i wartościami:
        </para>

        <table frame="all" cellpadding="5" id="zend.db.adapter.list-describe.metadata">
            <title>Metadane zwracane przez describeTable()</title>
            <tgroup cols="3" align="left" colsep="1" rowsep="1">
                <thead>
                    <row>
                        <entry>Klucz</entry>
                        <entry>Typ</entry>
                        <entry>Opis</entry>
                    </row>
                </thead>
                <tbody>
                    <row>
                        <entry><constant>SCHEMA_NAME</constant></entry>
                        <entry>(string)</entry>
                        <entry>Nazwa schematu bazy danych, w którym tabela się znajduje.</entry>
                    </row>
                    <row>
                        <entry><constant>TABLE_NAME</constant></entry>
                        <entry>(string)</entry>
                        <entry>Nazwa tabeli zawierającej daną kolumnę.</entry>
                    </row>
                    <row>
                        <entry><constant>COLUMN_NAME</constant></entry>
                        <entry>(string)</entry>
                        <entry>Nazwa kolumny.</entry>
                    </row>
                    <row>
                        <entry><constant>COLUMN_POSITION</constant></entry>
                        <entry>(integer)</entry>
                        <entry>Liczba porządkowa wskazująca na miejsce kolumny w tabeli.</entry>
                    </row>
                    <row>
                        <entry><constant>DATA_TYPE</constant></entry>
                        <entry>(string)</entry>
                        <entry>Nazwa typu danych dozwolonych w kolumnie.</entry>
                    </row>
                    <row>
                        <entry><constant>DEFAULT</constant></entry>
                        <entry>(string)</entry>
                        <entry>Domyślna wartość kolumny (jeśli istnieje).</entry>
                    </row>
                    <row>
                        <entry><constant>NULLABLE</constant></entry>
                        <entry>(boolean)</entry>
                        <entry>
                            <constant>TRUE</constant> jeśli columna dopuszcza wartości
                            <acronym>SQL</acronym>
                            <constant>NULL</constant>, <constant>FALSE</constant>
                            jeśli kolumna zawiera 
                            ograniczenie <constant>NOT</constant> <constant>NULL</constant>.
                        </entry>
                    </row>
                    <row>
                        <entry><constant>LENGTH</constant></entry>
                        <entry>(integer)</entry>
                        <entry>
                            Dopuszczalny rozmiar kolumny, w formie zgłoszonej przez serwer
                            bazy danych.
                        </entry>
                    </row>
                    <row>
                        <entry><constant>SCALE</constant></entry>
                        <entry>(integer)</entry>
                        <entry>
                            Skala typów <acronym>SQL</acronym> NUMERIC lub
                            <constant>DECIMAL</constant>.
                        </entry>
                    </row>
                    <row>
                        <entry><constant>PRECISION</constant></entry>
                        <entry>(integer)</entry>
                        <entry>
                            Precyzja typów <acronym>SQL</acronym> NUMERIC
                            lub <constant>DECIMAL</constant>.
                        </entry>
                    </row>
                    <row>
                        <entry><constant>UNSIGNED</constant></entry>
                        <entry>(boolean)</entry>

                        <entry>
                            <constant>TRUE</constant> jeśli typ danych liczbowych ma klauzulę
                            <constant>UNSIGNED</constant>.
                        </entry>
                    </row>
                    <row>
                        <entry><constant>PRIMARY</constant></entry>
                        <entry>(boolean)</entry>

                        <entry>
                            <constant>TRUE</constant> jeśli kolumna jest częścią
                            klucza pierwotnego tabeli.
                        </entry>
                    </row>
                    <row>
                        <entry><constant>PRIMARY_POSITION</constant></entry>
                        <entry>(integer)</entry>
                        <entry>
                            Liczba porządkowa (min. 1) oznaczająca pozycję
                            kolumny w kluczu pierwotnym.
                        </entry>
                    </row>
                    <row>
                        <entry><constant>IDENTITY</constant></entry>
                        <entry>(boolean)</entry>

                        <entry>
                            <constant>TRUE</constant> jeśli kolumna korzysta
                            z wartości automatycznie generowanych.
                        </entry>
                    </row>
                </tbody>
            </tgroup>
        </table>

        <note>
            <title>Pole metadanych IDENTITY w różnych systemach zarządzania bazą danych</title>
            <para>
                Pole metadanych IDENTITY zostało wybrane jako ogólny
                termin określający relację do klucza tabeli. Może być znany
                pod następującymi nazwami:
            </para>
            <itemizedlist>
                <listitem>
                    <para>
                        <constant>IDENTITY</constant> - DB2, MSSQL
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <constant>AUTO_INCREMENT</constant> - MySQL
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <constant>SERIAL</constant> - PostgreSQL
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <constant>SEQUENCE</constant> - Oracle
                    </para>
                </listitem>
            </itemizedlist>
        </note>

        <para>
            Jeśli w bazie danych nie istnieje tabela i schemat o podanych nazwach to
            <methodname>describeTable()</methodname> zwraca pustą tablicę. 
        </para>

    </sect2>

    <sect2 id="zend.db.adapter.closing">

        <title>Zamykanie połączenia</title>

        <para>
            Najczęściej nie ma potrzeby zamykać połączenia z bazą danych. <acronym>PHP</acronym>
            automatycznie czyści wszystkie zasoby pod koniec działania. Rozszerzenia bazy danych są
            zaprojektowane w taki sposób aby połączenie zostało zamknięte w momencie usunięcia 
            referencji do obiektu zasobu.
        </para>

        <para>
            Jednak w przypadku skryptu <acronym>PHP</acronym>, którego czas wykonania jest znaczący, 
            który inicjuje wiele połączeń z bazą danych, może zajść potrzeba
            ręcznego zamknięcia połączenia aby ograniczyć wykorzystanie
            zasobów serwera bazy danych. Aby wymusić zamknięcie połączenia
            z bazą danych należy użyć metody adaptera <methodname>closeConnection()</methodname>.
        </para>

        <para>
            Od wersji 1.7.2 istnieje możliwość sprawdzenia czy w obecnej chwili
            połączenie z serwerem bazy danych występuje za pomocą metody
            <methodname>isConnected()</methodname>.
            Jej rezultat oznacza czy zasób połączenia został ustanowiony i nie został zamknięty.
            Ta metoda nie jest zdolna sprawdzić zamknięcia połączenia od strony
            serwera bazy danych.
            Dzięki użyciu jej w wewnętrznych metodach zamykających połączenie
            można dokonywać wielokrotnego zamknięcia połączenia bez ryzyka wystąpienia błędów.
            Przed wersją 1.7.2 było to możliwe jedynie w
            przypadku adapterów <acronym>PDO</acronym>.
        </para>

        <example id="zend.db.adapter.closing.example">
            <title>Zamknięcie połączenia z bazą danych</title>
            <programlisting language="php"><![CDATA[
$db->closeConnection();
]]></programlisting>
        </example>

        <note>
            <title>Czy Zend_Db wspiera połączenia trwałe (Persistent Connections)?</title>
            <para>
                Tak, trwałe połączenia są wspierane poprzez flagę <property>persistent</property>
                ustawioną na wartość <constant>TRUE</constant> w konfiguracji
                adaptera <classname>Zend_Db</classname> (a nie sterownika bazy danych).
            </para>

            <example id="zend.db.adapter.connecting.persistence.example">
                <title>Użycie flagi stałego połączenia z adapterem Oracle</title>
                <programlisting language="php"><![CDATA[
$db = Zend_Db::factory('Oracle', array(
    'host'       => '127.0.0.1',
    'username'   => 'webuser',
    'password'   => 'xxxxxxxx',
    'dbname'     => 'test',
    'persistent' => true
));
]]></programlisting>
            </example>

            <para>
                Należy zwrócić uwagę, że używanie połączeń stałych może zwiększyć ilość
                nieużywanych połączeń na serwerze bazy danych. Przez to, korzyści
                w wydajności (wynikające z wykluczenia potrzeby nawiązywania połączenia
                przy każdym żądaniu) mogą zostać skutecznie przeważone przez problemy
                spowodowane przez tą technikę.
            </para>
            <para>
                Połączenia z bazą danych mają stany. Oznacza to, że pewne obiekty na serwerze
                bazy danych istnieją w kontekście sesji. Chodzi o blokady, zmienne użytkownika,
                tabele tymczasowe, informacje o ostatnio wykonanym zapytaniu - takie jak
                ilość pobranych wierszy, ostatnia wygenerowana wartość identyfikatora. W 
                przypadku użycia połączeń stałych istnieje ryzyko, że aplikacja uzyska
                dostęp do niepoprawnych bądź zastrzeżonych danych, które zostały utworzone
                podczas poprzedniego żądania.
            </para>
            <para>
                W obecnym stanie jedynie adaptery Oracle, DB2 oraz <acronym>PDO</acronym> 
                (zgodnie z dokumentacją <acronym>PHP</acronym>) wspierają połączenia stałe
                w <classname>Zend_Db</classname>.
            </para>
        </note>

    </sect2>

    <sect2 id="zend.db.adapter.other-statements">

        <title>Wykonywanie innych poleceń na bazie danych</title>

        <para>
            Podczas tworzenia kodu może zajść potrzeba uzyskania dostępu bezpośrednio do 
            obiektu połączenia tak jak udostępnia to używane rozszerzenie bazy danych 
            <acronym>PHP</acronym>. Niektóre rozszerzenia mogą oferować funkcje nieodzwierciedlone
            w metodach klasy <classname>Zend_Db_Adapter_Abstract</classname>.
        </para>

        <para>
            Wszystkie polecenia <acronym>SQL</acronym> w <classname>Zend_Db</classname>
            są wywoływane poprzez instrukcje preparowane (prepared statement). Jednak niektóre
            funkcje bazy danych są z nimi niekompatybilne. Polecenia <acronym>DDL</acronym> takie 
            jak CREATE czy ALTER nie mogą być wywoływane w ten sposób w MySQL. Dodatkowo polecenia
            <acronym>SQL</acronym> nie korzystają z 
            <ulink url="http://dev.mysql.com/doc/refman/5.1/en/query-cache-how.html">cache'u zapytań
            MySQL (MySQL Query Cache)</ulink> dla wersji wcześniejszej niż MySQL 5.1.17.
        </para>

        <para>
            Większość rozszerzeń baz danych <acronym>PHP</acronym> umożliwia wywoływanie poleceń
            <acronym>SQL</acronym> bez preparowania. W przypadku <acronym>PDO</acronym> jest to
            możliwe poprzez metodę <methodname>exec()</methodname>. Aby uzyskać dostęp do obiektu
            połączenia odpowiedniego rozszerzenia <acronym>PHP</acronym> należy wywołać metodę
            <methodname>getConnection()</methodname>.
        </para>

        <example id="zend.db.adapter.other-statements.example">
            <title>Wywołanie polecenia niepreparowanego dla adaptera PDO</title>
            <programlisting language="php"><![CDATA[
$result = $db->getConnection()->exec('DROP TABLE bugs');
]]></programlisting>
        </example>

        <para>
            W podobny sposób można korzystać z innych metod szczególnych dla konkretnego
            rozszerzenia <acronym>PHP</acronym>. Należy jednak mieć w pamięci, iż w ten
            sposób ogranicza się tworzoną aplikację do współpracy z interfejsem oferowanym
            jedynie przez konkretne rozszerzenie konkretnej bazy danych.
        </para>

        <para>
            W przyszłych wersjach <classname>Zend_Db</classname> planowane jest umieszczanie 
            dodatkowych metod służących do obejmowania funkcjonalności wspólnych dla wielu
            rozszerzeń baz danych <acronym>PHP</acronym> ale wsteczna kompatybilność zostanie
            zachowana.
        </para>

    </sect2>

    <sect2 id="zend.db.adapter.server-version">
        <title>Pobranie wersji serwera</title>

        <para>
            Począwszy od wersji 1.7.2 można pobrać wersję serwera bazy danych w formie podobnej do
            numeru wersji <acronym>PHP</acronym> tak aby można było skorzystać z funkcji
            <methodname>version_compare()</methodname>. Jeśli taka informacja nie jest dostępna
            to zostanie zwrócona wartość <constant>NULL</constant>.
        </para>

        <example id="zend.db.adapter.server-version.example">
            <title>Weryfikacja wersji serwera przed wywołaniem zapytania</title>
            <programlisting language="php"><![CDATA[
$version = $db->getServerVersion();
if (!is_null($version)) {
    if (version_compare($version, '5.0.0', '>=')) {
        // wykonanie zapytania
    } else {
        // wykonanie innego zapytania
    }
} else {
    // wersja serwera niedostępna
}
]]></programlisting>
        </example>
    </sect2>

    <sect2 id="zend.db.adapter.adapter-notes">

        <title>Informacje o konkretnych adapterach</title>

        <para>
            Ten akapit wymienia różnice pomiędzy klasami adapterów, z istnienia których
            należy sobie zdawać sprawę.
        </para>

        <sect3 id="zend.db.adapter.adapter-notes.ibm-db2">
            <title>IBM DB2</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy 'Db2'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzenia <acronym>PHP</acronym> ibm_db2.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        IBM DB2 wspiera sekwencje oraz klucze automatycznie zwiększające.
                        Przez to argumenty dla <methodname>lastInsertId()</methodname> są
                        opcjonalne. Jeśli nie poda się argumentów adapter zwróci ostatnią
                        wartość wygenerowaną dla klucza automatycznego. Jeśli argumenty zostaną
                        podane to adapter zwróci ostatnią wartość wygenerowaną przez sekwencję
                        o nazwie zgodnej z konwencją 
                        '<emphasis>tabela</emphasis>_<emphasis>kolumna</emphasis>_seq'.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.mysqli">
            <title>MySQLi</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy 'Mysqli'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzenia <acronym>PHP</acronym> mysqli.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        MySQL nie wspiera sekwencji więc <methodname>lastInsertId()</methodname>
                        ignoruje argumenty i zwraca ostatnią wartość wygenerowaną dla klucza
                        automatycznego. Metoda <methodname>lastSequenceId()</methodname>
                        zwraca <constant>NULL</constant>.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.oracle">
            <title>Oracle</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy 'Oracle'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzenia <acronym>PHP</acronym> oci8.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Oracle nie wspiera kluczy automatycznych więc należy podać nazwę
                        sekwencji w metodzie <methodname>lastInsertId()</methodname> lub
                        <methodname>lastSequenceId()</methodname>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Rozszerzenie Oracle nie wspiera parametrów pozycyjnych. Należy
                        używać nazwanych parametrów.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Obecnie, opcja <constant>Zend_Db::CASE_FOLDING</constant> nie jest
                        zaimplementowana w tym adapterze. Aby użyć tej opcji z Oracle zalecane
                        jest korzystanie z adaptera <acronym>PDO</acronym> OCI.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Domyślnie pola LOB są zwracane jako obiekty OCI-Lob. Można ustawić
                        pobieranie ich w formie łańcuchów znakowych dla wszystkich żądań
                        poprzez opcję sterownika '<property>lob_as_string</property>'. Aby
                        jednorazowo pobrać obiekt Lob jako string należy użyć metody
                        <methodname>setLobAsString(boolean)</methodname>  na adapterze lub
                        na obiekcie zapytania.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.sqlsrv">
            <title>Microsoft SQL Server</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy 'Sqlsrv'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzenia <acronym>PHP</acronym> sqlsrv.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Microsoft <acronym>SQL</acronym> Server nie wspiera sekwencji więc
                        <methodname>lastInsertId()</methodname> ignoruje argument określający
                        klucz pierwotny i zwraca ostatnią wartość wygenerowaną przez automatyczny
                        klucz (jeśli jest podana nazwa tabeli) lub identyfikator zwrócony przez
                        ostatnie polecenie INSERT. Metoda <methodname>lastSequenceId()</methodname> 
                        zwraca <constant>NULL</constant>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <classname>Zend_Db_Adapter_Sqlsrv</classname> ustawia opcję
                        <constant>QUOTED_IDENTIFIER ON</constant> bezpośrednio po połączeniu się
                        z bazą danych <acronym>SQL</acronym> Server. To powoduje, że sterownik
                        zaczyna używać standardowego znaku cudzysłowu (<emphasis>"</emphasis>) jako
                        ograniczenia identyfikatorów zamiast - charakterystycznych dla produktu 
                        Microsoftu - nawiasów kwadratowych.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Jednym z kluczy podawanych do tablicy opcji może być
                        <property>driver_options</property>
                        dzięki czemu można skorzystać z wartości podanych w dokumentacji
                        <acronym>SQL</acronym> Server
                        <ulink url="http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx">
                        http://msdn.microsoft.com/en-us/library/cc296161(SQL.90).aspx</ulink>. 
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Dzięki metodzie <methodname>setTransactionIsolationLevel()</methodname>
                        można ustawić poziom izolacji transakcji dla bieżącego połączenia.
                        Rozpoznawane wartości
                        to <constant>SQLSRV_TXN_READ_UNCOMMITTED</constant>,
                        <constant>SQLSRV_TXN_READ_COMMITTED</constant>,
                        <constant>SQLSRV_TXN_REPEATABLE_READ</constant>,
                        <constant>SQLSRV_TXN_SNAPSHOT</constant> or
                        <constant>SQLSRV_TXN_SERIALIZABLE</constant>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Począwszy od Zend Framework 1.9 minimalną wspieraną wersją rozszerzenia
                        <acronym>PHP</acronym> Microsoft <acronym>SQL</acronym> Server jest
                        1.0.1924.0. a dla <acronym>MSSQL</acronym> Server Native
                        Client - wersja 9.00.3042.00.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.pdo-ibm">
            <title>PDO dla IBM DB2 oraz Informix Dynamic Server (IDS)</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy '<classname>Pdo_Ibm</classname>'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzeń <acronym>PHP</acronym> pdo and pdo_ibm.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Należy używać wersji 1.2.2 lub wyższej rozszerzenia PDO_IBM. Zaleca się
                        uaktualnienie wcześniejszych wersji poprzez <acronym>PECL</acronym>.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.pdo-mssql">
            <title>PDO Microsoft SQL Server</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy '<classname>Pdo_Mssql</classname>'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzeń <acronym>PHP</acronym> pdo and pdo_dblib.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Microsoft <acronym>SQL</acronym> Server nie wspiera sekwencji więc
                        <methodname>lastInsertId()</methodname> ignoruje argument określający
                        klucz pierwotny i zwraca ostatnią wartość wygenerowaną przez automatyczny
                        klucz (jeśli jest podana nazwa tabeli) lub identyfikator zwrócony przez
                        ostatnie polecenie INSERT. Metoda <methodname>lastSequenceId()</methodname> 
                        zwraca <constant>NULL</constant>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        W przypadku pracy z łańcuchami znaków unicode zakodowanych w sposób inny
                        niż UCS-2 (czyli również w formie UTF-8), może zajść potrzeba dokonania 
                        konwersji w kodzie aplikacji lub przechowywania
                        danych w kolumnach binarnych.
                        Aby uzyskać więcej informacji można skorzystać z
                        <ulink url="http://support.microsoft.com/kb/232580">Microsoft's Knowledge
                        Base</ulink>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <classname>Zend_Db_Adapter_Pdo_Mssql</classname> ustawia opcję
                        <constant>QUOTED_IDENTIFIER ON</constant> bezpośrednio po połączeniu się
                        z bazą danych <acronym>SQL</acronym> Server. To powoduje, że sterownik
                        zaczyna używać standardowego znaku cudzysłowu (<emphasis>"</emphasis>) jako
                        ograniczenia identyfikatorów zamiast - charakterystycznych dla produktu 
                        Microsoftu - nawiasów kwadratowych.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Adapter rozpoznaje klucz <property>pdoType</property> w tablicy opcji. Jego
                        wartość może wynosić "mssql" (domyślnie), "dblib", "freetds", lub "sybase".
                        To wpływa na prefiks <acronym>DSN</acronym> używany przez
                        adapter do utworzenia łańcucha połączenia.
                        Wartości "freetds" oraz "sybase" powodują utworzenie prefiksu
                        "sybase:" używanego przez biblioteki 
                        <ulink url="http://www.freetds.org/">FreeTDS</ulink>.
                        Należy zapoznać się z informacjami dotyczącymi prefiksów
                        <acronym>DSN</acronym>
                        używanymi przez ten sterownik pod adresem
                        <ulink url="http://www.php.net/manual/en/ref.pdo-dblib.connection.php">
                        http://www.php.net/manual/en/ref.pdo-dblib.connection.php</ulink>
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.pdo-mysql">
            <title>PDO MySQL</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy '<classname>Pdo_Mysql</classname>'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzeń <acronym>PHP</acronym> pdo and pdo_mysql.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        MySQL nie wspiera sekwencji więc <methodname>lastInsertId()</methodname>
                        ignoruje argumenty i zwraca ostatnią wartość wygenerowaną dla klucza
                        automatycznego. Metoda <methodname>lastSequenceId()</methodname>
                        zwraca <constant>NULL</constant>.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.pdo-oci">
            <title>PDO Oracle</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy '<classname>Pdo_Oci</classname>'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzeń <acronym>PHP</acronym> pdo and pdo_oci.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Oracle nie wspiera kluczy automatycznych więc należy podać nazwę
                        sekwencji w metodzie <methodname>lastInsertId()</methodname> lub
                        <methodname>lastSequenceId()</methodname>.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.pdo-pgsql">
            <title>PDO PostgreSQL</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy '<classname>Pdo_Pgsql</classname>'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzeń <acronym>PHP</acronym> pdo and pdo_pgsql.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        PostgreSQL wspiera sekwencje oraz klucze automatyczne. Przez to
                        podawanie argumentów w <methodname>lastInsertId()</methodname> jest
                        opcjonalne. Jeśli nie poda się argumentów adapter zwróci ostatnią
                        wartość wygenerowaną dla klucza automatycznego. Jeśli argumenty zostaną
                        podane to adapter zwróci ostatnią wartość wygenerowaną przez sekwencję
                        o nazwie zgodnej z konwencją 
                        '<emphasis>tabela</emphasis>_<emphasis>kolumna</emphasis>_seq'. 
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.pdo-sqlite">
            <title>PDO SQLite</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Aby uzyskać ten adapter poprzez metodę <methodname>factory()</methodname> 
                        należy użyć nazwy '<classname>Pdo_Sqlite</classname>'.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzeń <acronym>PHP</acronym> pdo and pdo_sqlite.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        SQLite nie wspiera sekwencji więc <methodname>lastInsertId()</methodname>
                        ignoruje argumenty i zwraca ostatnią wartość wygenerowaną dla klucza
                        automatycznego. Metoda <methodname>lastSequenceId()</methodname>
                        zwraca <constant>NULL</constant>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Aby połączyć się z bazą danych SQLite2 należy podać 
                        <command>'sqlite2' => true</command> jako jeden z elementów
                        tablicy parametrów podczas tworzenia instancji adaptera
                        <classname>Pdo_Sqlite</classname>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Aby połączyć się z bazą danych SQLite rezydującą w pamięci
                        należy podać <command>'dbname' => ':memory:'</command> jako
                        jeden z elementów tablicy parametrów podczas tworzenia instancji adaptera
                        <classname>Pdo_Sqlite</classname>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Starsze wersje sterownika <acronym>PHP</acronym> SQLite nie wspierają
                        poleceń PRAGMA niezbędnych dla zachowania krótkich nazw kolumn w 
                        wynikach zapytania. W przypadku wystąpienia problemów z zapytaniami JOIN
                        polegających na zwracaniu wyników z nazwami kolumn w postaci 
                        "tabela.kolumna" zaleca się aktualizację <acronym>PHP</acronym> do 
                        najnowszej wersji.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

        <sect3 id="zend.db.adapter.adapter-notes.firebird">
            <title>Firebird/Interbase</title>
            <itemizedlist>
                <listitem>
                    <para>
                        Ten adapter używa rozszerzenia <acronym>PHP</acronym> php_interbase.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Firebird/Interbase nie wspiera kluczy automatycznych więc należy podać nazwę
                        sekwencji w metodzie <methodname>lastInsertId()</methodname> lub
                        <methodname>lastSequenceId()</methodname>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        Obecnie, opcja <constant>Zend_Db::CASE_FOLDING</constant> nie jest
                        zaimplementowana w tym adapterze. Identyfikatory bez znaków ograniczających
                        są zwracane w postaci wielkich liter.
                    </para>
                </listitem>

                <listitem>
                    <para>
                        Nazwa adaptera to <classname>ZendX_Db_Adapter_Firebird</classname>.
                    </para>
                    <para>
                        Należy pamiętać o użyciu parametru adapterNamespace z wartością
                        <classname>ZendX_Db_Adapter</classname>.
                    </para>
                    <para>
                        Zaleca się aktualizację pliku <filename>gds32.dll</filename> (lub 
                        odpowiednika wersji linux) dostarczanym z <acronym>PHP</acronym> do wersji odpowiadającej
                        serwerowi bazy danych. Dla Firebird odpowiednikiem 
                        <filename>gds32.dll</filename> jest
                        <filename>fbclient.dll</filename>.
                    </para>
                    <para>
                        Domyślnie wszystkie identyfikatory (nazwy tabel, kolumn) są zwracane 
                        wielkimi literami.
                    </para>
                </listitem>
            </itemizedlist>
        </sect3>

    </sect2>

</sect1>
<!--
vim:se ts=4 sw=4 et:
-->
