<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<id>http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Pou%C5%BEit%C3%AD_kompozitn%C3%ADch_typ%C5%AF_v_PL%2FpgSQL</id>
	<title>Použití kompozitních typů v PL/pgSQL - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Pou%C5%BEit%C3%AD_kompozitn%C3%ADch_typ%C5%AF_v_PL%2FpgSQL"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Pou%C5%BEit%C3%AD_kompozitn%C3%ADch_typ%C5%AF_v_PL/pgSQL&amp;action=history"/>
	<updated>2026-05-12T22:37:06Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Pou%C5%BEit%C3%AD_kompozitn%C3%ADch_typ%C5%AF_v_PL/pgSQL&amp;diff=539&amp;oldid=prev</id>
		<title>imported&gt;Pavel v 1. 9. 2013, 08:16</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Pou%C5%BEit%C3%AD_kompozitn%C3%ADch_typ%C5%AF_v_PL/pgSQL&amp;diff=539&amp;oldid=prev"/>
		<updated>2013-09-01T08:16:26Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;PostgreSQL umožňuje vytvářet a používat tzv kompozitní typy. Ty se do Postgresu dostaly ještě v době před SQL, kdy se hledal mezičlánek mezi relačními a OOP databázemi. Pomocí kompozitních typů lze jednodušší OOP systém vytvořit (bez podpory virtuálních metod a atypickým zápisem volání metod - metody jsou přetížené funkce, neexistují kontejnery). V praxi se ovšem kompozitní typy příliš neosvědčily - řada programátorů před nimi varuje, a to hlavně před jejich použitím v tabulkách. Přístup k položkám v kompozitních typech je o něco málo obtížnější a generické klientské aplikace mohou mít s těmito typy problémy. Což ale neznamená, že nemůžeme tyto typy úspěšně použít v PL/pgSQL. &lt;br /&gt;
&lt;br /&gt;
Kompozitní typy v PostgreSQL neumožňují definovat omezení (CONSTRAINTS). Na druhou stranu umožňují definovat přetypování (CASTS).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# CREATE TYPE address AS (street varchar(32), house_number integer);&lt;br /&gt;
CREATE TYPE&lt;br /&gt;
&lt;br /&gt;
postgres=# CREATE TABLE users(name varchar, address address);&lt;br /&gt;
CREATE TABLE&lt;br /&gt;
&lt;br /&gt;
postgres=# INSERT INTO users VALUES(&amp;#039;Pavel&amp;#039;, ROW(&amp;#039;Skalice&amp;#039;, 12));&lt;br /&gt;
INSERT 0 1&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT * FROM users;&lt;br /&gt;
 name  │   address    &lt;br /&gt;
───────┼──────────────&lt;br /&gt;
 Pavel │ (Skalice,12)&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT name, (address).* &lt;br /&gt;
              FROM users &lt;br /&gt;
             WHERE (address).street = &amp;#039;Skalice&amp;#039;;&lt;br /&gt;
 name  │ street  │ house_number &lt;br /&gt;
───────┼─────────┼──────────────&lt;br /&gt;
 Pavel │ Skalice │           12&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# CREATE INDEX ON users(((address).street));&lt;br /&gt;
CREATE INDEX&lt;br /&gt;
&lt;br /&gt;
postgres=# EXPLAIN SELECT name, (address).*&lt;br /&gt;
                      FROM users&lt;br /&gt;
                     WHERE (address).street = &amp;#039;Skalice&amp;#039;;&lt;br /&gt;
                                  QUERY PLAN                                   &lt;br /&gt;
───────────────────────────────────────────────────────────────────────────────&lt;br /&gt;
 Index Scan using users_street_idx on users  (cost=0.12..8.14 rows=1 width=39)&lt;br /&gt;
   Index Cond: (((address).street)::text = &amp;#039;Skalice&amp;#039;::text)&lt;br /&gt;
(2 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Na první pohled je vše ook - nicméně generické aplikace občas mohou mít problémy s neznámými datovými typy (zde typ &amp;quot;address&amp;quot;). Na základě varování jsem si nikdy nezvykl používat kompozitní typy v tabulkách. Jelikož ale pracuji s uloženými procedurami a pracuji s entitami, kde se typicky opakují určité kombinace položek, kde by bylo použítí kompozitních typů poměrně přirozené a redukují opakující se kód v uložených procedurách, vytvořil jsem si jednoduchou metodu, jak jednoduše kompozitní typy v PL/pgSQL používat. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TYPE public.identity_type AS&lt;br /&gt;
(&lt;br /&gt;
        cust_ref_num            public.custrefnum_type,&lt;br /&gt;
        owner_surname           public.ownersurname_type,&lt;br /&gt;
        owner_first_name        public.ownerfirstname_type,&lt;br /&gt;
        owner_company_name              public.ownercompname_type,&lt;br /&gt;
        ico_number              public.iconumber_type&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Chybějící omezení jsem vyřešil používáním vlastních konstruktorů. V kódu funkce lze jednoduše implementovat i složitějsí kontroly, které by byly zápisem v CHECK hůře čitelné.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Funkcionalni konstruktor typu public.identity - vytvori a validuje hodnotu.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
CREATE OR REPLACE FUNCTION public._identity(&lt;br /&gt;
                                        cust_ref_num public.custrefnum_type DEFAULT NULL,&lt;br /&gt;
                                        owner_surname public.ownersurname_type DEFAULT NULL,&lt;br /&gt;
                                        owner_first_name public.ownerfirstname_type DEFAULT NULL,&lt;br /&gt;
                                        owner_company_name public.ownercompname_type DEFAULT NULL,&lt;br /&gt;
                                        ico_number public.iconumber_type DEFAULT NULL)&lt;br /&gt;
RETURNS public.identity_type AS $$&lt;br /&gt;
DECLARE result public.identity_type;&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF owner_first_name IS NOT NULL AND owner_surname IS NULL THEN&lt;br /&gt;
    RAISE EXCEPTION &amp;#039;When FirstName is known, then SurName should be known too&amp;#039;;&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF owner_surname IS NULL AND owner_company_name IS NULL THEN&lt;br /&gt;
    RAISE EXCEPTION &amp;#039;Owner is not known&amp;#039;;&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF owner_surname IS NOT NULL AND owner_company_name IS NOT NULL THEN&lt;br /&gt;
    RAISE EXCEPTION &amp;#039;Owner should be person or company, not both&amp;#039;;&lt;br /&gt;
  END IF;&lt;br /&gt;
  result := (cust_ref_num,&lt;br /&gt;
                        owner_surname,&lt;br /&gt;
                        owner_first_name,&lt;br /&gt;
                        owner_company_name,&lt;br /&gt;
                        ico_number)::public.identity_type;&lt;br /&gt;
  RETURN result;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Konstruktorem mohu rovnou vytvářet i záznam v databázi (merge funkce jsou vysvětlené níže):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Konstruktor pro zadani odchoziho cps_formu.&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
CREATE OR REPLACE FUNCTION outgoing.store_cps_form(&lt;br /&gt;
                                                        cps_operator public.operatorid_type,&lt;br /&gt;
                                                        cps_operator_appear public.operatorid_type,&lt;br /&gt;
                                                        csc_code public.csccode_type,&lt;br /&gt;
                                                        address public.address_type,&lt;br /&gt;
                                                        identity public.identity_type,&lt;br /&gt;
                                                        calling_line_id public.callinglineid_type,&lt;br /&gt;
                                                        cps_service_type public.cpsservicetype_type,&lt;br /&gt;
                                                        complex_order public.complexorder_type,&lt;br /&gt;
                                                        sending_date date DEFAULT &amp;#039;infinity&amp;#039;)&lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
DECLARE&lt;br /&gt;
  worker outgoing.cps_forms;&lt;br /&gt;
  seqvalue int;&lt;br /&gt;
BEGIN&lt;br /&gt;
  seqvalue := nextval(&amp;#039;outgoing.cps_forms_id_seq&amp;#039;::regclass);&lt;br /&gt;
  worker.id := seqvalue;&lt;br /&gt;
  worker.inserted := LOCALTIMESTAMP;&lt;br /&gt;
  worker.cps_operator := store_cps_form.cps_operator;&lt;br /&gt;
  worker.cps_operator_appear := store_cps_form.cps_operator_appear;&lt;br /&gt;
  worker.csc_code := store_cps_form.csc_code;&lt;br /&gt;
  worker := outgoing.merge(worker, store_cps_form.address);&lt;br /&gt;
  worker := outgoing.merge(worker, store_cps_form.identity);&lt;br /&gt;
  worker.calling_line_id := store_cps_form.calling_line_id;&lt;br /&gt;
  worker.cps_service_type := store_cps_form.cps_service_type;&lt;br /&gt;
  worker.complex_order := store_cps_form.complex_order;&lt;br /&gt;
  worker.sending_date := store_cps_form.sending_date;&lt;br /&gt;
  INSERT INTO outgoing.cps_forms VALUES(worker.*);&lt;br /&gt;
  RETURN seqvalue;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 Pouziti funkce:&lt;br /&gt;
&lt;br /&gt;
  SELECT outgoing.store_cps_form(201, 202, &amp;#039;1234&amp;#039;,&lt;br /&gt;
                             address := public._address(house_number_a := &amp;#039;10&amp;#039;, city := &amp;#039;Prague&amp;#039;, street_name := &amp;#039;Americka&amp;#039;, district := &amp;#039;Stredocesky kraj&amp;#039;, post_code := &amp;#039;11150&amp;#039;),&lt;br /&gt;
                             identity := public._identity(owner_surname := &amp;#039;Stehule&amp;#039;, owner_first_name := &amp;#039;Pavel&amp;#039;, cust_ref_num := &amp;#039;Ahoj&amp;#039;),&lt;br /&gt;
                             calling_line_id := 123456789,&lt;br /&gt;
                             cps_service_type := &amp;#039;both&amp;#039;,&lt;br /&gt;
                             complex_order := &amp;#039;yes&amp;#039;);&lt;br /&gt;
*/&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Z výše uvedeného kódu je zřejmé, že se nespoléhám na pořadí parametrů, ale používám pojmenované parametry (kód je delší, ale je mnohem popisnější, a v případě úprav konstruktoru je mnohem menší šance na zavlečení chyb z důvodu nesprávného pořadí parametrů).&lt;br /&gt;
&lt;br /&gt;
Použití konstruktorů je výhodnější přímé operace s tabulkami (v tomto případě INSERT). &lt;br /&gt;
* Jsou k dispozici pojmenované parametry - syntaxe je o něco čitelnější a robustnější než u příkazu INSERT.&lt;br /&gt;
* Je volán kód, který obaluje založení nového záznamu - logiku, která by musela být v triggerech, mohu přesunout do konstruktoru.&lt;br /&gt;
* Mohu mít více konstruktorů a mohu volit mezi nimi podle potřeby (mohu mít jednodušší triggery, které nelze jednoduše parametrizovat - triggery primárně používám pouze pro kontroly a pro audit).&lt;br /&gt;
&lt;br /&gt;
V PostgreSQL existuje ke každé tabulce automatický kompozitní typ. Pro všechny tabulky, resp. pro všechny kompozitní typy obsahující jiný kompopzitní typ mám připravenou funkci &amp;quot;merge&amp;quot;, která zajistí zkopírování položek do cílového typu - fakticky zajistí rozbalení kompozitního typu. Vytvoření těchto funkcí je jednoduché - a kód je čitelný a přehledný.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION outgoing.merge(t outgoing.np_forms, s public.identity_type)&lt;br /&gt;
RETURNS outgoing.np_forms AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  t.cust_ref_num := s.cust_ref_num;&lt;br /&gt;
  t.owner_surname := s.owner_surname;&lt;br /&gt;
  t.owner_first_name := s.owner_first_name;&lt;br /&gt;
  t.owner_company_name := s.owner_company_name;&lt;br /&gt;
  t.ico_number := s.ico_number;&lt;br /&gt;
  RETURN t;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION outgoing.merge(t outgoing.np_forms, s public.np_id_info_type)&lt;br /&gt;
RETURNS outgoing.np_forms AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  t.np_id := s.np_id;&lt;br /&gt;
  t.sp_id_out := s.sp_id_out;&lt;br /&gt;
  t.sp_id_in := s.sp_id_in;&lt;br /&gt;
  RETURN t;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION outgoing.merge(t outgoing.cps_forms, s public.address_type)&lt;br /&gt;
RETURNS outgoing.cps_forms AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  t.house_number_a := s.house_number_a;&lt;br /&gt;
  t.house_number_b := s.house_number_b;&lt;br /&gt;
  t.street_name := s.street_name;&lt;br /&gt;
  t.city := s.city;&lt;br /&gt;
  t.city_section := s.city_section;&lt;br /&gt;
  t.district := s.district;&lt;br /&gt;
  t.post_code := s.post_code;&lt;br /&gt;
  RETURN t;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Intuitivně se navrhnou i inverzní funkce - v jednodušším případě je lze aktivovat přetypováním:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Cast funkce obsluhuji pretypovani z tab. typu obsahujicich polozky, ktere nalezneme&lt;br /&gt;
 * v identity_type a address_type na identity_type a address_type. Pouziti je nasnade,&lt;br /&gt;
 * Pro identity_type a address_type jsou k dispozici funkce to_xml, ktere serializuji&lt;br /&gt;
 * data v techto strukturach do xml.&lt;br /&gt;
 *&lt;br /&gt;
 * Pozn.: Na prvni pohled jsou tyto funkce identicke s funkcemi &amp;#039;outgoing.merge&amp;#039;.&lt;br /&gt;
 * To je ovsem klamny dojem - lisi se v deklaracich promennych. Bohuzel anyelement&lt;br /&gt;
 * v PL/pgSQL nelze pouzit pro compozitni typy, record zase se nechova jako template.&lt;br /&gt;
 * Tudiz kod funkce je nutno duplikovat - vzhledem k jeho jednoduchosti a stabilite&lt;br /&gt;
 * to necitim jako problem - (Pavel).&lt;br /&gt;
 *&lt;br /&gt;
 */&lt;br /&gt;
CREATE OR REPLACE FUNCTION outgoing.cast_to_address(s outgoing.np_forms)&lt;br /&gt;
RETURNS public.address_type AS $$&lt;br /&gt;
DECLARE t public.address_type;&lt;br /&gt;
BEGIN&lt;br /&gt;
  t.house_number_a := s.house_number_a;&lt;br /&gt;
  t.house_number_b := s.house_number_b;&lt;br /&gt;
  t.street_name&amp;gt;:= s.street_name;&lt;br /&gt;
  t.city := s.city;&lt;br /&gt;
  t.city_section := s.city_section;&lt;br /&gt;
  t.district := s.district;&lt;br /&gt;
  t.post_code := s.post_code;&lt;br /&gt;
  RETURN t;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION outgoing.cast_to_identity(s outgoing.np_forms)&lt;br /&gt;
RETURNS public.identity_type AS $$&lt;br /&gt;
DECLARE t public.identity_type;&lt;br /&gt;
BEGIN&lt;br /&gt;
  t.cust_ref_num := s.cust_ref_num;&lt;br /&gt;
  t.owner_surname := s.owner_surname;&lt;br /&gt;
  t.owner_first_name := s.owner_first_name;&lt;br /&gt;
  t.owner_company_name := s.owner_company_name;&lt;br /&gt;
  t.ico_number := s.ico_number;&lt;br /&gt;
  RETURN t;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE;&lt;br /&gt;
   &lt;br /&gt;
CREATE OR REPLACE FUNCTION outgoing.cast_to_np_id_info(s outgoing.np_forms)&lt;br /&gt;
RETURNS public.np_id_info_type AS $$&lt;br /&gt;
DECLARE t public.np_id_info_type;&lt;br /&gt;
BEGIN&lt;br /&gt;
  t.np_id := s.np_id;&lt;br /&gt;
  t.sp_id_out := s.sp_id_out;&lt;br /&gt;
  t.sp_id_in := s.sp_id_in;&lt;br /&gt;
  RETURN t;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
CREATE CAST (outgoing.np_forms AS public.address_type)&lt;br /&gt;
   WITH FUNCTION outgoing.cast_to_address(outgoing.np_forms);&lt;br /&gt;
&lt;br /&gt;
CREATE CAST (outgoing.np_forms AS public.identity_type)&lt;br /&gt;
   WITH FUNCTION outgoing.cast_to_identity(outgoing.np_forms);&lt;br /&gt;
&lt;br /&gt;
CREATE CAST (outgoing.np_forms AS public.np_id_info_type)&lt;br /&gt;
   WITH FUNCTION outgoing.cast_to_np_id_info(outgoing.np_forms);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pro kompozitní typy si lze napsat &amp;quot;metody&amp;quot; - zde např. serializaci do XML&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Serializace typu &amp;#039;identity_type&amp;#039; do xml.&lt;br /&gt;
 * &lt;br /&gt;
 */&lt;br /&gt;
CREATE OR REPLACE FUNCTION fbuilder.cast_to_xml(idt public.identity_type)&lt;br /&gt;
RETURNS xml AS $$&lt;br /&gt;
  SELECT xmlconcat(xmlelement(NAME &amp;quot;serviceContractOwner&amp;quot;,&lt;br /&gt;
                                             xmlforest($1.owner_surname AS &amp;quot;ownerSurname&amp;quot;,&lt;br /&gt;
                                                       $1.owner_first_name AS &amp;quot;ownerFirstName&amp;quot;,&lt;br /&gt;
                                                       $1.owner_company_name AS &amp;quot;ownerCompanyName&amp;quot;))&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Při serializaci obsahujícího objektu použiji přetypování a výše uvedenou metodu (resp. funkci použitou jako metodu).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/*&lt;br /&gt;
 * Serializace outgoing.np_forms do xml&lt;br /&gt;
 *&lt;br /&gt;
 * Pozn.: Jelikoz tato funkce vyzaduje tri parametry, tak neni vytvoreno&lt;br /&gt;
 * pretypovani do xml&lt;br /&gt;
 */&lt;br /&gt;
CREATE OR REPLACE FUNCTION fbuilder.to_xml(f outgoing.np_forms, order_nr int, sequence_nr int)&lt;br /&gt;
RETURNS xml AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  RETURN xmlelement(NAME &amp;quot;npProvide&amp;quot;,&lt;br /&gt;
            xmlattributes(to_char(order_nr, &amp;#039;FMO00000000&amp;#039;) AS &amp;quot;orderNr&amp;quot;,&lt;br /&gt;
                           sequence_nr AS &amp;quot;sequenceNr&amp;quot;,&lt;br /&gt;
                           f.np_service_type AS &amp;quot;npServiceType&amp;quot;,&lt;br /&gt;
                           f.complex_order AS &amp;quot;complexOrder&amp;quot;),&lt;br /&gt;
            xmlelement(NAME &amp;quot;recipientOperator&amp;quot;, fbuilder.to_xml(f.recipient_op)),&lt;br /&gt;
            xmlelement(NAME &amp;quot;losingOperator&amp;quot;, fbuilder.to_xml(f.losing_op)),&lt;br /&gt;
            f::identity_type::xml, ---&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;br /&gt;
            f::address_type::xml,&lt;br /&gt;
            xmlforest(f.cust_ref_num AS &amp;quot;customerReferenceNumber&amp;quot;,&lt;br /&gt;
                      f.ico_number AS &amp;quot;icoNumber&amp;quot;),&lt;br /&gt;
            f.directory_number::xml,&lt;br /&gt;
            f.ddi_range::xml,&lt;br /&gt;
            f::np_id_info_type::xml,&lt;br /&gt;
            xmlforest(fbuilder.attachment_to_xml(f.np_id_attachment) AS &amp;quot;npIDattachment&amp;quot;),&lt;br /&gt;
            xmlelement(NAME &amp;quot;portActivationDate&amp;quot;, f.port_activation_date::xml),&lt;br /&gt;
            xmlelement(NAME &amp;quot;portActivationTime&amp;quot;, f.port_activation_time::xml));&lt;br /&gt;
END&lt;br /&gt;
$$ LANGUAGE plpgsql STRICT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hierarchie kompozitních typů respektuje definici v DTD:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT installatioFirstName ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT installatioSurname ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT installationCompanyName ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT installationdescription ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT minspeed ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT newActivationDate ( date ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT newActivationTime ( time ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT postCode ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT serviceInstallationOwner ( (installatioSurname, installatioFirstName) | (installationCompanyName, icoNumber) ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT speedAccept ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT streetName ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT time ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT uircode ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT billingaccount ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT city ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT citySection ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT contactName ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT contactPerson ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT contactemail ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT district ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT speedRequired ( #PCDATA ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT expectedActivationDate ( date ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT expectedActivationTime ( time ) &amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT newPortTime (time)&amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT serviceContractOwner ((ownerSurname, ownerFirstName) | ownerCompanyName)&amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT serviceDisconnectionTime (time)&amp;gt;&lt;br /&gt;
&amp;lt;!ELEMENT installationAddress ( uircode?,(housenumberB | ( housenumberA , housenumberB?)), streetName, city, citySection?, district, postCode, contactName?, contactPerson?, contactemail?, installationdescription?, speedRequired? )&amp;gt;&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;!ELEMENT npProvide (recipientOperator, losingOperator, serviceContractOwner, installationAddress, customerReferenceNumber, icoNumber?, (directoryNumber+ | ddiRange+), npIDinfo?, npIDattachment?, portActivationDate, portA&lt;br /&gt;
ctivationTime)&amp;gt;&lt;br /&gt;
&amp;lt;!ATTLIST npProvide&lt;br /&gt;
        orderNr CDATA #REQUIRED&lt;br /&gt;
        sequenceNr CDATA #REQUIRED&lt;br /&gt;
        npServiceType (geog | non_geog) #REQUIRED&lt;br /&gt;
        complexOrder (yes | no | Yes | No) #REQUIRED&lt;br /&gt;
        hasLLU (true | false) #IMPLIED&lt;br /&gt;
&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Pro tento typ dat se použití kompozitních typů osvědčilo. Jde to docela dobře dohromady - pomocí kompozitních typů se získáváme jednoduché statické OOP - to je ale adekvátní a vhodné vůči relační databázi, a v případě, že zadání vychází z DTD dokumentu, které vytváří statický model, tak si docela dobře vystačíme i s možnostmi, které (ohledně OOP) jsou v PL/pgSQL.&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>