<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<id>http://postgres.cz/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=194.255.108.253</id>
	<title>PostgreSQL - Příspěvky [cs]</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=194.255.108.253"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/wiki/Speci%C3%A1ln%C3%AD:P%C5%99%C3%ADsp%C4%9Bvky/194.255.108.253"/>
	<updated>2026-09-24T13:07:25Z</updated>
	<subtitle>Příspěvky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Automatic_execution_plan_caching_in_PL/pgSQL&amp;diff=292</id>
		<title>Automatic execution plan caching in PL/pgSQL</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Automatic_execution_plan_caching_in_PL/pgSQL&amp;diff=292"/>
		<updated>2007-12-30T20:03:24Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:Articles]]&lt;br /&gt;
Translate by Hana Kabilková&lt;br /&gt;
&lt;br /&gt;
Few years ago, when I wrote article about PL/pgSQL I found in documentation recommendation to not use string &#039;now&#039; in this language. I accepted this, I tried it and  it didn&#039;t work, but I realy didn&#039;t know why. I thought that it was because of bad transtation to byte-code of  actually pseudofunction value, which made this problem (time returned of string &#039;now&#039; agreed with function translation). &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
INSERT INTO (..) VALUES(&#039;now&#039;) -- don&#039;t use with plpgsql!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here I first deviate. Lexical and syntactic analysis PL/pgSQL of functions is making only once, in first call function within the scope of login. The result is syntactic tree which is saves in session cache. Something like translation to byte-code in PL/pgSQL doesn&#039;t exist, syntactic tree is input for interpreter, it means that PL/pgSQL is classic interpreter which stands on lex, yacc generator. Power PL/pgSQL isn&#039;t in speed but in his accouplement with SQL. Because of this accouplement  implementation PL/pgSQL can be as easy as it doesn&#039;t contain even simple evaluation of expressions. Everithing possible is send to interpreter SQL. Interpreter PL/pgSQL resolves only variables and operative construction.&lt;br /&gt;
&lt;br /&gt;
In saving SQL term in PL/pgSQL is using type PLpqSQL_expr(plpgsql.h). For another description are important only array char *query (contains text sql statement) and void *plan (indicator on cach execute plan SQL statement). Execute plan is generating only once, in first requirement evaluation SQL expression.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (expr-&amp;gt;plan == NULL) &lt;br /&gt;
  exec_prepare_plan(estate, expr) &lt;br /&gt;
.... &lt;br /&gt;
rc = SPI_execute_plan(expr-&amp;gt;plan, ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Caching is important. In easier expression generating of plan can be longer than expression evaluation. Measure difference between first and second start of PL/pgSQL function. You can write this time on score now generating execute plans. Caching solved problem with effective executing PL/pgSQL function and gave us two problems: Caching execute plans aren&#039;t shared and persistanced too (star application are slower, increasing of memory usage (resolution is f.e. pgool), caching execute plans can be sometimes not adequate and their executing makes run-time error.&lt;br /&gt;
&lt;br /&gt;
When the plan is ready, it&#039;s save in cache and there it can be changed until log off or compilation on function. In PostgreSQL there is no attribute of analogy attribute WITH RECOMPILE MSSQL. Fortunately wrong executing plans errors are exception and only because of two reasons. &lt;br /&gt;
&lt;br /&gt;
The first reason is change of database structure - when we canceled some of data objects (table, sequence) which was used in function, after the first called, the following called of function ends with error. The resolution isn&#039;t new object with the same name and type, because new object get new (different) oid (object identificator). Nobody think about canceled tabel during the operation, but every body want canceled temporary table - and because of this, is this problem bonded in ToDo with temporary tables. The resolution is all of temporary tables makes befor first called PL/pgSQL function and then not canceled, only clean. (Sometimes it trepan, especially when you convered from MSSQL to PostgresSQL, where the mechanism of transfering recordsets of procedurs is very different).&lt;br /&gt;
&lt;br /&gt;
We can make temporary tables in the body of function because executing plan saves into the cache in the first moment of using the objet, not in the time of translation.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION ... &lt;br /&gt;
  BEGIN&lt;br /&gt;
    PERFORM 1 &lt;br /&gt;
       FROM pg_catalog.pg_class c &lt;br /&gt;
            JOIN &lt;br /&gt;
            pg_catalog.pg_namespace n ON n.oid = c.relnamespace &lt;br /&gt;
      WHERE c.relkind IN (&#039;r&#039;,&#039;&#039;) AND c.relname = &#039;tmptab&#039; &lt;br /&gt;
        AND pg_catalog.pg_table_is_visible(c.oid) AND n.nspname LIKE &#039;pg_temp%&#039;; &lt;br /&gt;
    IF FOUND THEN &lt;br /&gt;
      TRUNCATE tmptab; &lt;br /&gt;
    ELSE &lt;br /&gt;
      CREATE TEMP TABLE tmptab(... &lt;br /&gt;
    END IF;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second reason is dynamic questions. Their executing plan isn&#039;t caching, but their result can make error on difrent place.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo() RETURNS void AS $$ &lt;br /&gt;
  DECLARE _t varchar[] = &#039;{integer, varchar}&#039;; &lt;br /&gt;
    _v varchar; _r record; &lt;br /&gt;
  BEGIN &lt;br /&gt;
    FOR _i IN 1 .. 2 LOOP &lt;br /&gt;
      FOR _r IN EXECUTE &#039;SELECT 1::&#039;||_t[_i]||&#039; AS _x&#039; LOOP &lt;br /&gt;
        _v := _r._x; &lt;br /&gt;
      END LOOP; &lt;br /&gt;
    END LOOP; &lt;br /&gt;
END; $$ LANGUAGE plpgsql; &lt;br /&gt;
select foo();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Start of function ends with error (_v := _r._x;) in second passing of cycle FOR _i IN .. ERROR: type of &amp;quot;_r._x&amp;quot; does not match that when preparing the plan.&lt;br /&gt;
&lt;br /&gt;
Why? Command of assigning contains SQL expression (every expression in PL/pgSQL is SQL expression). In first interace it makes executing plan, which supposed that value_r._x is integer, in second interace is type varchar, so the plan in cach is not adequate. The resolution is have as many assigning expressions as many possible executing plans combinations. At the firs look absurd code of function is right.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
FOR _i IN 1 .. 2 LOOP &lt;br /&gt;
  FOR _r IN EXECUTE &#039;SELECT 1::&#039;||_t[_i]||&#039; AS _x&#039; LOOP &lt;br /&gt;
    IF _i = 1 THEN _v := _r._x; &lt;br /&gt;
    ELSIF _i = 2 THEN _v := _r._x; &lt;br /&gt;
    END IF; &lt;br /&gt;
  END LOOP; &lt;br /&gt;
END LOOP;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
I tell you that this behavior of PL/pgSQL is free for me. I know reason of the problem and I can set up for that. But definitive resolution (regeneration of plan when the error was detected) can be make. I tried macras PG_TRY(), PG_CATCH() and PG_END_TRY() for fixation of errorand regenerated wrong plan without loss of speed. It&#039;s only the question of time, when someone catch this resolution and make patch. Probablty not in version 8.1.&lt;br /&gt;
&lt;br /&gt;
Second divagation : to show where is the problem with &#039;now&#039; I have to mention proces of generation executing plan SQL expression.&lt;br /&gt;
&lt;br /&gt;
One of a period of preparing the plan is reduction constants and simplification functions (f.e. 2+2=4, True Or whatever=True, alternative immutable functions with constant argument of function consequence - and this is &#039;now&#039; case (backend/optimizer/util/clause.c - evaluate_function()), alternative value NULL STRICT function, if some of their arguments is NULL, etc.&lt;br /&gt;
&lt;br /&gt;
In our case was &#039;now&#039; replaced by argument which was saved in executing plan and it was still same repeated evaluated. Normaly it don&#039;t have to make some problems. Expect PL/pgSQL PostgreSQL doesn&#039;t contain any tools which can cache executing plans. Exception was PL/pgSQL, where this problem was first detected (and we still have to be carefull about this). &lt;br /&gt;
&lt;br /&gt;
I can&#039;t remember if I ever used &#039;now&#039;. I automatically use magic parameters CURENT_DATE a CURRENT_TIMESTAMP which haven&#039;t got problem with cache. And if I only for nostalgia want to use &#039;now&#039; , i have to do this only with parameter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DECLARE d date; BEGIN d := &#039;now&#039;; INSERT INTO (..) VALUES(d); ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Why? For function with parameter is optimizer so short ( here function datein()). PL/pgSQL doesn&#039;t do something like optimalization, so it can&#039;t detected that it he is the parameter, and string &#039;now&#039; will have to retype on corresponding type only in time of evaluating expression and consequence will be corresponding time of evaluating expression.&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=Diskuse:Santiago&amp;diff=304</id>
		<title>Diskuse:Santiago</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Diskuse:Santiago&amp;diff=304"/>
		<updated>2007-12-28T08:10:55Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: /* pozdrav */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ahoj Pavle pelegrinos!&lt;br /&gt;
&lt;br /&gt;
do Santiaga jdu za týden, vyrážím ze severozápadního Slezska, z Jesenicka.&lt;br /&gt;
&lt;br /&gt;
Díky za postřehy na Tvé stránce. Budou se mi možná hodit. Pokud bych měl na tebe e-mail, napsal bych Ti to osobně.&lt;br /&gt;
&lt;br /&gt;
S pozdravem&lt;br /&gt;
Tom Hradil&lt;br /&gt;
thradil@jeseniky.org&lt;br /&gt;
tel: 603 231 624&lt;br /&gt;
&lt;br /&gt;
:: Zdarec, kontakt na mne je na [[Pavel Stěhule]]&lt;br /&gt;
&lt;br /&gt;
== pozdrav  ==&lt;br /&gt;
&lt;br /&gt;
Pavle,&lt;br /&gt;
díky za poznámky!&lt;br /&gt;
Vyrazila jsem letos ze Saint Jean a až za tři roky dokončím svá študýrování, chtěla bych jít od nás z Moravy.Máš moc pěkné fotky!&lt;br /&gt;
Bylo to ... krásné,že? Spoustu věcí jsem zapomněla a postupně se v určitých okamžicích vynořují, asi abych zůstala vděčná. &lt;br /&gt;
Jinak nemůžu zapomenout na oči poutníků.&lt;br /&gt;
Radost při putování, které začalo po návratu a všem potenciálním poutníkům odvahu vydat se na cestu!&lt;br /&gt;
L.&lt;br /&gt;
fangaia@seznam.cz&lt;br /&gt;
:: Díky, Jo, Camino je druhej svět a vůbec putování. Teď chodím po republice, už jsem přešel Čechy i Moravu z Jablůnkova do Aše, a stojí to taky za to. Jen by to chtělo nějaký refídže. Měj se hezky a dobrý rok přeji.&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=Postgres_Informix&amp;diff=377</id>
		<title>Postgres Informix</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Postgres_Informix&amp;diff=377"/>
		<updated>2007-12-15T09:17:42Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Na otázku, zda PostgreSQL může nahradit Informix odpověděl Jeff Larsen. Dovolil jsem si jeho mail přeložit a citovat. Originál je k dohledání v archivu konference.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    * From: &amp;quot;Jeff Larsen&amp;quot; &amp;lt;jlar310 ( at ) gmail ( dot ) com&amp;gt;&lt;br /&gt;
    * To: Chad ( dot ) Hendren ( at ) sun ( dot ) com&lt;br /&gt;
    * Subject: Re: PostgresSQL vs. Informix&lt;br /&gt;
    * Date: Wed, 28 Nov 2007 17:11:00 -0600&lt;br /&gt;
&lt;br /&gt;
Používáme Informix a aktuálně jsem pracoval na studii proveditelnosti přechodu&lt;br /&gt;
na PostgreSQL. Ověřil jsem si kvalitu dokumentace Postgresu a úroveň podpory &lt;br /&gt;
mailing listech.&lt;br /&gt;
&lt;br /&gt;
Obě databáze mají své pro a proti. Jelikož mám PostgreSQL na jiném hardware,&lt;br /&gt;
nelze jednoduše porovnat výkon, ale myslím si, že Informix je rychlejší a má &lt;br /&gt;
robustnější plánovač. To jest, dělá to co má, aniž by potřeboval pomoc v takových&lt;br /&gt;
věcech jako je přetypování. Řekl bych, že Postgres je na 75 až 80% rychlosti&lt;br /&gt;
Informixu. Nechci začínat flame. Je to můj odhad, a jsem si jistý, že se stejným&lt;br /&gt;
hardwarem a s trochou ladění by byl výkon Postresql lepší.&lt;br /&gt;
&lt;br /&gt;
Co je na Informixu vynikající je jeho plně synchronní replikace, kde potvrzení&lt;br /&gt;
transakce na primárním serveru garantuje potvrzení transakce na sekundárním &lt;br /&gt;
serveru. Vysoká dostupnost je pro nás kritická, a co je mi známo, toto je slabinou&lt;br /&gt;
PostgreSQL. Jistě, Postgres podporuje replikaci, ale tato podpora ještě není vyzrálá&lt;br /&gt;
pro enterprise prostředí.&lt;br /&gt;
&lt;br /&gt;
Informix má podstatně pokročilejší online zálohování, ne nepodobné PITR zálohování&lt;br /&gt;
Postgresu, které ovšem není tak závislé na nízkoúrovňových systémových nástrojích.&lt;br /&gt;
Dále podporuje inkrementální backup, takže nemusíme provádět pokaždé dump celé&lt;br /&gt;
databáze. Informix nabízí ještě další zálohovací utilitu onbar, ale s tou nemám&lt;br /&gt;
žádné zkušenosti, takže ji nebudu komentovat.&lt;br /&gt;
&lt;br /&gt;
Co hovoří pro Postgres. Cena, jasně. Miluji, když mohu ušetřit sto tisíc dolarů.&lt;br /&gt;
PostgreSQL více respektuje SQL standardy, takže je snažší portovat aplikace. Postgres&lt;br /&gt;
má také některé lepší vestavěné funkce a větší možnosti indexování. PostgreSQL má&lt;br /&gt;
širší nabídku jazyků pro uložené procedury (python, perl), a konečně PostgresSQL&lt;br /&gt;
umožňuje dědičnost tabulek, což Informix neumožňuje. &lt;br /&gt;
&lt;br /&gt;
Určitě nejpůsobivější věcí na Postgresu jsou jeho konference (mailing lists). Podpora&lt;br /&gt;
Informixu je v pohodě, nicméně trubci na zákaznické podpoře nejsou vývojáři, kteří &lt;br /&gt;
vědí o co jde. Zatraceně, vývojáři Postgresu odpověděli na mé otázky přímo a to ještě&lt;br /&gt;
o víkendu. Nevím kolik by stála podpora za přímý přístup k těm nejvyšším guru.&lt;br /&gt;
&lt;br /&gt;
Rád bych, kdyby moje odpověď mohla být důkladnější. Jsem si jistý, že jsem zapomněl&lt;br /&gt;
zmínit řadu suprových věcí na Postgresu, ale jak už jsem řekl, moje hodnocení&lt;br /&gt;
je pouze orientační. Právě řešíme, zda-li přejít, jako firma, na Postgres. Používáme&lt;br /&gt;
open source a uvítal bych, kdybychom používali i PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
Jeff&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=Diskuse:Aktuality&amp;diff=232</id>
		<title>Diskuse:Aktuality</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Diskuse:Aktuality&amp;diff=232"/>
		<updated>2007-12-15T07:45:15Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: nova verze, nova diskuze&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=Features83&amp;diff=376</id>
		<title>Features83</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Features83&amp;diff=376"/>
		<updated>2007-12-12T21:07:16Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: /* Výkon ve Windows */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=PostgreSQL 8.3 Feature List=&lt;br /&gt;
Tento seznam obsahuje popis většiny, nikoliv všech, nových funkcí verze 8.3. Pro přehlednost je tento popis rozčleněn do několika skupin podle účelu. Podrobnější informace jsou v dokumentaci PostgreSQL a v poznámkách k verzi (Release Notes). Skutečně kompaktní informace najdete v tabulce podporovaných funkcí (feature matrix), která je ovšem pouze v anglickém jazyce.&lt;br /&gt;
&lt;br /&gt;
==Výkon==&lt;br /&gt;
===Konzistentní výkon===&lt;br /&gt;
Tyto funkce zvyšují schopnost PostgreSQL dodržet konzistentní časy odezvy bez ohledu na zatížení serveru.&lt;br /&gt;
;HOT&lt;br /&gt;
:Heap Only Tuple (HOT) dramaticky snižuje problémy s údržbou databáze spojené s častými změnami dat, snižuje nutnost spouštění VACUUM a poskytuje znatelně vyšší výkon některých aplikací.&lt;br /&gt;
;Asynchronní potvrzování&lt;br /&gt;
:V tomto režimu COMMIT nečeká na potvrzení fyzického zápisu na disk. Za cenu rizika potenciální ztráty posledních transakcí, v případě výpadku systému, získáme lepší dobu odezvy. Přesto je jištěna integrita dat. Tento režim je určen pro operace, které lze bezpečně opakovat .. např. import databáze, import dat a je lokální (vztahuje se pouze na ty uživatele, kteří se do tohoto režimu přepnou).&lt;br /&gt;
;Vyrovnání zátěže způsobené checkpointy&lt;br /&gt;
:V případě, že je server silně vytížen, dojde k pozdržení checkpointů a k utlumení jejich frekvence. Tím se snižuje negativní vliv checkpointů na dobu odezvy. &lt;br /&gt;
;Strategie Just-in-time zápisu na disk&lt;br /&gt;
:Na základě statistik a aktuální aktivity je dynamicky odhadnuto, kolik čistých paměťových stránek bude potřeba a kolik jich bude nutné vyčistit (zapsat na disk).&lt;br /&gt;
&lt;br /&gt;
===Zrychlení===&lt;br /&gt;
Díky řadě úprav se dosáhlo citelného zrychlení některých operací.&lt;br /&gt;
;Zkrácení času obnovy&lt;br /&gt;
:Nutný čas pro obnovu databáze z Write Ahead logu se zkrátil redukcí počtu nutných I/O operací.&lt;br /&gt;
;Cyklický buffer v meziúložišti řádků&lt;br /&gt;
:Dramaticky urychluje menší spojení tabulek typu merge join tím, že lépe využívá paměť a nedochází tak k zápisům na disk. &lt;br /&gt;
;Rychlejší porovnávání v LIKE/ILIKE&lt;br /&gt;
:Zrychlila se operace částečného porovnání (partial match) zejména v případech, kdy se používá vícebajtové kódování.&lt;br /&gt;
;Top-N řazení&lt;br /&gt;
:Dramaticky zrychluje řazení v případě, že výsledek dotazu je omezen klauzulí LIMIT.&lt;br /&gt;
;Opožděné přidělování XID (Lazy XID Assignment)&lt;br /&gt;
:Znatelně zvyšuje výkon těch databází, kde výrazně převažuje čtení nad zápisem (případně vůbec nedochází k zápisu) a to tak, že identifikační číslo transakce přiřadí pouze v případě potřeby (dojde k změně dat). &lt;br /&gt;
;Možnost určení ceny funkce&lt;br /&gt;
:Nám dovolí připojit ke každé funkci odhad náročnosti funkce (cena) a počtu vrácených řádků, což v důsledku znamená reálnější (lepší) prováděcí plán. &lt;br /&gt;
&lt;br /&gt;
===Rozsáhlé databáze===&lt;br /&gt;
Nasledující funkce dovolí uživatelům provozovat i poměrně velké datové sklady v PostgreSQL.&lt;br /&gt;
;Synchronizované čtení&lt;br /&gt;
:V originále tzv. piggybacking table scan, je synchronizované souběžné čtení jedné tabulky více uživateli (jeden přečtený blok se pošle více uživatelům). Tato technika významně redukuje celkovou potřebu IO operací (pozn. překladatele - piggyback se používá spolu se slovem jízda ve smyslu jízdy na nárazníku, rámu (přeneseně jízdy načerno nebo zadarmo). V tomto případě se data načítají z disku pro jednoho uživatele a okamžitě distribuují všem dalším uživatelům, kteří je v tu chvíli také potřebují. &lt;br /&gt;
;Ochrana L2 Cache&lt;br /&gt;
:Nově optimalizovaný kód snižuje četnost zahození obsahu cache CPU, které způsobuje zpomalení současně zpracovávaných dotazů.&lt;br /&gt;
;Zkrácení délky hlavičky typu Varlena&lt;br /&gt;
:Úpravou formátu datového typu Varlena, který se v PostgreSQL používá pro uložení všech hodnot s proměnnou velikostí, dochází ke snížení velikosti databáze zhruba o 20%.&lt;br /&gt;
&lt;br /&gt;
===Výkon ve Windows===&lt;br /&gt;
Nemůžeme zapomenout na naše uživatele, kteří používají Windows. PostgreSQL 8.3 posouvá Windows do první ligy podporovaných platforem.&lt;br /&gt;
;Podpora MS Visual C++&lt;br /&gt;
:PostgreSQL lze přeložit nejen v MinGW (vývojové prostředí umožňující snažší portování aplikací z OS typu Unix), ale i v Microsoft Visual C++. Použití překladače fy. Microsoft by mělo zvýšit výkon a stabilitu na platformách této firmy.&lt;br /&gt;
;Přepracování startovního kódu serveru&lt;br /&gt;
:Drasticky snižuje spotřebu paměti procesu postmaster, což umožňuje paralelní běh více obslužných procesů (kteří obslouží více klientů).&lt;br /&gt;
&lt;br /&gt;
==Administrace==&lt;br /&gt;
Administrace PostgreSQL je jednodušší než u proprietárních databází, nicméně vždy je další prostor pro zlepšení. PostgreSQL obsahuje řadu nových funkcí zjednodušujících správu a poskytujících administrátorovi databáze podrobnější a obsáhlejší diagnostiku.  &lt;br /&gt;
;CSV Log Output&lt;br /&gt;
Volitelně lze zapisovat do logu v CSV formátu, což zjednodušuje tvorbu nástrojů na analýzu výkonu a ad-hoc auditů.&lt;br /&gt;
;Podpora SSPI GSSAPI&lt;br /&gt;
:Podpora autentifikačního systému Kerberos v PostgreSQL byla rozšířena o možnost použít bezpečnostní API: SSPI je průmyslový standard na platformě Windows a GSSAPI je průmyslový standard v prostředí Unix a Linux. Podpora těchto API by měla zjednodušit integraci uvnitř velkých  vnitropodnikových sítí.&lt;br /&gt;
;Lokální nastavení systémových proměnných&lt;br /&gt;
:Pro každou uživatelskou funkci lze určit specifické nastavení systémových proměnných. Kromě jiného se tím řeší bezpečnostní problém s přenastavením systémové proměnné search_path.&lt;br /&gt;
;Více procesové autovacuum&lt;br /&gt;
:Umožňuje paralelní běh servisního procesu, díky čemuž je autovacuum použitelné i v aplikacích s tisíci tabulkami.&lt;br /&gt;
;pgStandby&lt;br /&gt;
:Administrativní rutina zjednodušující provoz serveru ve Warm Standby režimu.&lt;br /&gt;
;ORDER BY Nulls First/Last&lt;br /&gt;
:Umožňuje vytvářet indexy, kde jsou řádky obsahující NULL umístěny na začátek nebo na konec indexu.&lt;br /&gt;
&lt;br /&gt;
==Vývoj==&lt;br /&gt;
===Vývoj aplikací===&lt;br /&gt;
Díky  celé řady úprav se PostgreSQL 8.3 může měřit s nejlepšími proprietárními databázemi v podpoře komplexních více vrstvých databázových aplikací. &lt;br /&gt;
;Fulltextové vyhledávání&lt;br /&gt;
:TSearch2 byl plně integrován do kódu jádra. Také došlo k pročištění API. Díky tomu se fulltext jednodušeji používá a snáze rozšiřuje o podporu nových jazyků, slovníků a systémů určujících relevanci.&lt;br /&gt;
;Odstranění neplatných plánů&lt;br /&gt;
:Uložené prováděcí plány mohou být odstraněny jak samotnou aplikací, tak automaticky, když jsou tabulky aktualizovány.&lt;br /&gt;
;Editovatelné kurzory (Updatable Cursors)&lt;br /&gt;
:Kurzory nyní podporují WHERE CURRENT OF, což umožňuje flexibilnější návrh aplikací postavených na použití kurzorů.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3&amp;gt;Nové datové typy&amp;lt;/h3&amp;gt;&lt;br /&gt;
;Typ XML&lt;br /&gt;
:Typ XML plně podporuje standard SQL/XML, který je součástí ANSI SQL:2003 včetně kontroly zápisu, typově bezpečných operací, funkcí generujících XML a XPath dotazů. Verze 8.3 obsahuje ještě další funkce umožňující export v XML.&lt;br /&gt;
;Typ UUID&lt;br /&gt;
:Typ UUID nese 128 bitovou unifikovaný (globální) jednoznačný identifikátor, který se uplatní zejména v distribuovaných aplikacích.&lt;br /&gt;
;Pole hodnot kompozitního typu &lt;br /&gt;
:Pole nyní může být vytvořeno pro kompozitní (složené typy), které obsahují více sloupců v jedné hodnotě, jako je typ tabulka nebo zákaznický typ.&lt;br /&gt;
;Výčtový typ&lt;br /&gt;
:Výčtový typ je určen seřazeným seznamem alternativních hodnot. Díky tomuto typu bude snažší migrace z MySQL do PostgreSQL.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;h3&amp;gt;Uložené procedury&amp;lt;/h3&amp;gt;&lt;br /&gt;
Tyto dvě nové funkce zvyšují použitelnost PL/pgSQL, což je náš nejpopulárnější jazyk pro tvorbu uložených procedur v PostgreSQL.&lt;br /&gt;
;RETURN QUERY&lt;br /&gt;
:Nyní lze v PL/pgSQL ještě jednodušeji vrátit výsledek dotazu v SRF funkci.&lt;br /&gt;
; Posuvné kurzory (Scrollable Cursors)&lt;br /&gt;
:PL/pgSQL nyní také podporuje posuvné kurzory, umožňující v PL/pgSQL procedurách dynamický pohyb kurzoru (vpřed i vzad).&lt;br /&gt;
&lt;br /&gt;
==Příslušenství==&lt;br /&gt;
Řada důležitých funkcí není podporována v základním kódu. Důvodem je snaha o udržení co nejmenšího jádra databáze, které se snáze udržuje. Existuje několik stovek volitelných doplňků, s kterými PostgreSQL umožňuje replikace, podporuje vysokou dostupnost, použití dalších programovacích jazyků, integraci aplikací a získává i některé další experimentální možnosti. Většina těchto doplňků je volně ke stažení z archivu [http://www.pgfoundry.org pgFoundry]. Část aplikací(modulů) z následujícího seznamu je již přepravena pro 8.3. Část modulů s 8.3 spolupracuje, ale ještě nevyužívá všechny možnosti, které jsou v 8.3. Tyto moduly se budou v následujícím období aktualizovat.&lt;br /&gt;
&lt;br /&gt;
;[https://developer.skype.com/SkypeGarage/DbProjects/pgbouncer pgBouncer]&lt;br /&gt;
:Tento více vláknový connection pooler umožňuje jedné PostgreSQL databázi udržovat až 100,000 spojení mezi aplikacemi a serverem.&lt;br /&gt;
;[https://developer.skype.com/SkypeGarage/DbProjects/PlProxy PL/Proxy]&lt;br /&gt;
:Interface pro distribuované horizontálně separované tabulky.&lt;br /&gt;
;[http://pgsnmpd.projects.postgresql.org/ pgSNMP]&lt;br /&gt;
:Standardní SNMP ovladač pro PostgreSQL zjednodušující monitorování serverů v síti.&lt;br /&gt;
;[http://code.google.com/p/sepgsql/downloads/list SEpgsql]&lt;br /&gt;
:Bezpečnostní doplněk postavený nad modelem a metodikou SELinuxu, který dovoluje aplikovat unifikovaný postup SELinuxu jak na OS tak na DBMS.&lt;br /&gt;
;[http://pgfoundry.org/projects/edb-debugger/ PL/pgSQL Debugger]&lt;br /&gt;
:Nový grafický nástroj podporující interaktivní ladění a krokování PL/pgSQL procedur.&lt;br /&gt;
;[http://pgfoundry.org/projects/pgpool/ pgPoolII]&lt;br /&gt;
:PgPoolII staví na úspěchu předchozí verze. Pomocí inteligentní replikace dotazů umožňuje datový partitioning.&lt;br /&gt;
;[http://bucardo.org/ Bucardo]&lt;br /&gt;
:V případě PostgreSQL první dostupný systém podporující multi-master asynchronní replikaci.&lt;br /&gt;
;[http://www.postgresql.at/english/pr_cybercluster_e.html CyberCluster]&lt;br /&gt;
:Nově otevřený open-source projekt integrující a rozšiřující několik stávajících nástrojů pro clustering, jako je pgCluster nebo pgPool.&lt;br /&gt;
;[http://www.slony.info/ Slony-I]&lt;br /&gt;
:Druhá verze Slony-I, našeho nejpopulárnějšího replikačního systému, nyní používá nové replikační API v PostgreSQL 8.3.&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=Automatick%C3%A1_kontrola_rodn%C3%A9ho_%C4%8D%C3%ADsla&amp;diff=360</id>
		<title>Automatická kontrola rodného čísla</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Automatick%C3%A1_kontrola_rodn%C3%A9ho_%C4%8D%C3%ADsla&amp;diff=360"/>
		<updated>2007-12-09T17:41:51Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: doplneni vyjimky&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Profesionální programování je více-méně o tvorbě knihoven. Programátor, ktrerý&lt;br /&gt;
staví stále na zelené louce nebude těžko bude produktivní. Klasické jazyky podporují&lt;br /&gt;
vytváření knihoven a modulů. K dispozici je dostatek kvalitních knihoven, kterými&lt;br /&gt;
se můžeme inspirovat při tvorbě vlastních knihoven.&lt;br /&gt;
&lt;br /&gt;
To bohužel neplatí u programování v prostředí uložených procedur. Zatím se nijak zvlášť &lt;br /&gt;
oproti ostatním neprosadil žádný koncept. Jednou z možností, jak organizovat vlastní&lt;br /&gt;
knihovny je vytváření vlastních &amp;quot;inteligentních&amp;quot; datových typů. Jelikož PostgreSQL &lt;br /&gt;
vzniklo v akademickém prostředí, které je co se týče potřeby ukládání specializovaných&lt;br /&gt;
dat podstatně pestřejší a náročnější než ekonomicko-finanční sféra (na kterou bylo &lt;br /&gt;
původně SQL cílené), PostgreSQL od samotného svého počátku disponovalo možností vytváření&lt;br /&gt;
vlastních datových formátů. V klasickém SQL je příliš úzký rejstřík datových typů, v&lt;br /&gt;
čehož důsledku musíme používat obecné typy TEXT nebo BLOB. Díky jejich obecnosti dokážeme&lt;br /&gt;
do nich uložit libovolnou textovou nebo binární hodnotu, ale nic víc. Tyto typy neposkytují&lt;br /&gt;
žádné další (specializované) operace.&lt;br /&gt;
&lt;br /&gt;
Vlastní datový typ v PostgreSQL může být implementován pouze v jazycích C nebo Java.&lt;br /&gt;
Kromě skutečných datových typů ještě existují složené datové typy (obdoba typu RECORD v&lt;br /&gt;
Pascalu). Kromě vlastních binárních datových typů můžeme definovat vlastní doménové&lt;br /&gt;
typy. Následující příklad obsahuje podporu rodného čísla právě pomocí domén.&lt;br /&gt;
&lt;br /&gt;
Rodné číslo je hodnota s celkem dost vysokou redundanci, zvlášť když máme k dispozici&lt;br /&gt;
datum narozeni a pohlaví osoby. Ve formuláři nedokáže zabránit záměrně falšované hodnotě,&lt;br /&gt;
ale s celkem s vysokou pravděpodobností dokaze zachytit překlepy. Vzhledem&lt;br /&gt;
k tomu, že obsahuje citlivé udaje (věk, pohlaví) předpokládá se, že bude v dohledné době&lt;br /&gt;
nahrazen neutrálním jedinečným identifikátorem osob (uvažuje se o číslu sociálního&lt;br /&gt;
zabezpečení).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION check_form_rodne_cislo(varchar, boolean)&lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  str_parts varchar[];&lt;br /&gt;
  num_parts integer[];&lt;br /&gt;
  after_1953 boolean;&lt;br /&gt;
  birthday_str varchar;&lt;br /&gt;
BEGIN&lt;br /&gt;
  SELECT INTO str_parts regexp_matches &lt;br /&gt;
     FROM regexp_matches($1, E&#039;^(\\d{2})(\\d{2})(\\d{2})(/)?(\\d{3,4})$&#039;);&lt;br /&gt;
  IF FOUND THEN &lt;br /&gt;
    -- Test existence lomitka v rodnem cisle rizeny druhym parametrem,&lt;br /&gt;
    -- ve vyznamu NULL (nema vyznam), true - povinne, false - nepovinne.&lt;br /&gt;
    IF $2 &amp;lt;&amp;gt; (str_parts[4] IS NOT NULL) THEN&lt;br /&gt;
      RETURN false;&lt;br /&gt;
    END IF;&lt;br /&gt;
    -- Test modulo 11 se provadi pouze tehdy, pokud existuje&lt;br /&gt;
    -- kontrolni cislice, tj. pocinaje rokem 1954.&lt;br /&gt;
    after_1953 := char_length(str_parts[5]) = 4;&lt;br /&gt;
    IF after_1953&lt;br /&gt;
                   AND to_number($1, CASE WHEN str_parts[4] IS NULL&lt;br /&gt;
                                          THEN &#039;9999999999&#039;&lt;br /&gt;
                                          ELSE &#039;999999/9999&#039; END) % 11 &amp;lt;&amp;gt; 0 THEN&lt;br /&gt;
      -- Nicmene z tohoto pravidla existuje vyjimka cca 1000                                                                                                           &lt;br /&gt;
      -- rodnych cisel, kdy modulo 9 mistneho cisla je rovno 10                                                                                                        &lt;br /&gt;
      -- a kontrolni cislice je 0                                                                                                                                      &lt;br /&gt;
      IF NOT (to_number($1, CASE WHEN str_parts[4] IS NULL&lt;br /&gt;
                                          THEN &#039;999999999&#039;&lt;br /&gt;
                                          ELSE &#039;999999/999&#039; END) % 11 = 10&lt;br /&gt;
          AND to_number($1, CASE WHEN str_parts[4] IS NULL&lt;br /&gt;
                                          THEN &#039;?????????9&#039;&lt;br /&gt;
                                          ELSE &#039;??????/???9&#039; END) = 0) THEN&lt;br /&gt;
            RETURN false;&lt;br /&gt;
      END IF;&lt;br /&gt;
    END IF;&lt;br /&gt;
    -- Kontrola validniho datumu, po roce 2004 se mohou&lt;br /&gt;
    -- k mesici pricitat hodnoty 20 (pro muze) a 70 pro zeny.&lt;br /&gt;
    num_parts := ARRAY[to_number(str_parts[1],&#039;99&#039;),&lt;br /&gt;
                       to_number(str_parts[2],&#039;99&#039;),&lt;br /&gt;
                       to_number(str_parts[3],&#039;99&#039;)];&lt;br /&gt;
    num_parts := ARRAY[CASE WHEN NOT after_1953 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            WHEN after_1953 AND num_parts[1] &amp;gt;= 54 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            ELSE 2000 + num_parts[1] END,&lt;br /&gt;
                       CASE WHEN num_parts[2] &amp;gt; 70&lt;br /&gt;
                            THEN num_parts[2] - 70&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 50&lt;br /&gt;
                            THEN num_parts[2] - 50&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 20&lt;br /&gt;
                            THEN num_parts[2] - 20&lt;br /&gt;
                            ELSE num_parts[2] END,&lt;br /&gt;
                       num_parts[3]];&lt;br /&gt;
    birthday_str := replace(to_char(num_parts[1],&#039;9999&#039;)&lt;br /&gt;
                            || to_char(num_parts[2],&#039;09&#039;) &lt;br /&gt;
                            || to_char(num_parts[3],&#039;09&#039;),&lt;br /&gt;
                               &#039; &#039;,&#039;&#039;);&lt;br /&gt;
    RETURN birthday_str = to_char(to_date(birthday_str, &#039;YYYYMMDD&#039;), &lt;br /&gt;
                                  &#039;YYYYMMDD&#039;);&lt;br /&gt;
  END IF;&lt;br /&gt;
  RETURN false;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
V případě binárního vlastního typu potřebujeme ještě konverzní funkce z&lt;br /&gt;
typu rc, rc1 a rc2 do typů date a boolean, které použijeme v definici&lt;br /&gt;
přetypování (CREATE CAST). Nic takového pro domény neexistuje. Nicméně mohu&lt;br /&gt;
napsat funkce to_date a to_boolean a ty používat. Aby nedocházelo k opakování&lt;br /&gt;
kódu, existuje implicitní přetypování rc1-&amp;gt;rc a rc2-&amp;gt;rc, což je korektní&lt;br /&gt;
(rc je obecnější než rc1 nebo rc2). Funkce to_bool a to_date musím volat&lt;br /&gt;
explicitně.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE DOMAIN rc  VARCHAR CHECK (check_form_rodne_cislo(value, null::boolean));&lt;br /&gt;
&lt;br /&gt;
-- Predpokladam validni vstup (overeny funkci check_form_rodne_cislo).&lt;br /&gt;
-- Typ parametru varchar je zvolen proto, protoze je predkem domen rc,&lt;br /&gt;
-- rc1 a rc2. Domeny se lisi vztahem vuci lomitku uvnitr rodneho cisla.&lt;br /&gt;
CREATE OR REPLACE FUNCTION to_date(rc)&lt;br /&gt;
RETURNS date AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  str_parts varchar[];&lt;br /&gt;
  num_parts integer[];&lt;br /&gt;
  after_1953 boolean;&lt;br /&gt;
  birthday_str varchar;&lt;br /&gt;
BEGIN&lt;br /&gt;
  SELECT INTO str_parts regexp_matches &lt;br /&gt;
     FROM regexp_matches($1, E&#039;^(\\d{2})(\\d{2})(\\d{2})(/)?(\\d{3,4})$&#039;);&lt;br /&gt;
  IF FOUND THEN &lt;br /&gt;
    after_1953 := char_length(str_parts[5]) = 4;&lt;br /&gt;
    -- Po roce 2004 se mohou k mesici pricitat &lt;br /&gt;
    -- hodnoty 20 (pro muze) a 70 pro zeny. Pred timto rokem&lt;br /&gt;
    -- se vzdy pricita 50 pro zeny&lt;br /&gt;
    num_parts := ARRAY[to_number(str_parts[1],&#039;99&#039;),&lt;br /&gt;
                       to_number(str_parts[2],&#039;99&#039;),&lt;br /&gt;
                       to_number(str_parts[3],&#039;99&#039;)];&lt;br /&gt;
    num_parts := ARRAY[CASE WHEN NOT after_1953 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            WHEN after_1953 AND num_parts[1] &amp;gt;= 54 &lt;br /&gt;
                            THEN 1900 + num_parts[1]&lt;br /&gt;
                            ELSE 2000 + num_parts[1] END,&lt;br /&gt;
                       CASE WHEN num_parts[2] &amp;gt; 70&lt;br /&gt;
                            THEN num_parts[2] - 70&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 50&lt;br /&gt;
                            THEN num_parts[2] - 50&lt;br /&gt;
                            WHEN num_parts[2] &amp;gt; 20&lt;br /&gt;
                            THEN num_parts[2] - 20&lt;br /&gt;
                            ELSE num_parts[2] END,&lt;br /&gt;
                       num_parts[3]];&lt;br /&gt;
    RETURN to_date(&lt;br /&gt;
                   replace(to_char(num_parts[1],&#039;9999&#039;)&lt;br /&gt;
                            || to_char(num_parts[2],&#039;09&#039;) &lt;br /&gt;
                            || to_char(num_parts[3],&#039;09&#039;),&lt;br /&gt;
                               &#039; &#039;,&#039;&#039;),&lt;br /&gt;
                   &#039;YYYYMMDD&#039;);&lt;br /&gt;
  END IF;&lt;br /&gt;
  RAISE NOTICE &#039;Incorrect format of type rodne cislo (value: %)&#039;, $1;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
-- Predpokladam validni vstup (overeny funkci check_form_rodne_cislo).&lt;br /&gt;
-- Typ parametru varchar je zvolen proto, protoze je predkem domen rc,&lt;br /&gt;
-- rc1 a rc2. Domeny se lisi vztahem vuci lomitku uvnitr rodneho cisla.&lt;br /&gt;
CREATE OR REPLACE FUNCTION to_bool(rc)&lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  str_part varchar[];&lt;br /&gt;
  birthday_mm integer;&lt;br /&gt;
BEGIN&lt;br /&gt;
  SELECT INTO str_part regexp_matches &lt;br /&gt;
     FROM regexp_matches($1, E&#039;^\\d{2}(\\d{2})\\d{2}/?\\d{3,4}$&#039;);&lt;br /&gt;
  IF FOUND THEN &lt;br /&gt;
    birthday_mm := to_number(str_part[1],&#039;09&#039;);&lt;br /&gt;
    RETURN CASE WHEN birthday_mm &amp;gt; 50 &lt;br /&gt;
                THEN false&lt;br /&gt;
                ELSE true END;&lt;br /&gt;
  END IF;&lt;br /&gt;
  RAISE NOTICE &#039;Incorrect format of type rodne cislo (value: %)&#039;, $1;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
-- Registrace domen &lt;br /&gt;
CREATE DOMAIN rc1 VARCHAR CHECK (check_form_rodne_cislo(value, false));&lt;br /&gt;
CREATE DOMAIN rc2 VARCHAR CHECK (check_form_rodne_cislo(value, true));&lt;br /&gt;
&lt;br /&gt;
-- vytvoreni konverznich pravidel, rc je zevseobecnenim rc1 a rc2&lt;br /&gt;
CREATE CAST (rc1  AS rc) WITHOUT FUNCTION AS IMPLICIT;&lt;br /&gt;
CREATE CAST (rc2  AS rc) WITHOUT FUNCTION AS IMPLICIT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Použití&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE foo (r rc1);&lt;br /&gt;
INSERT INTO foo(&#039;7307--/----&#039;);&lt;br /&gt;
-- ziskani narozeni z udaju v db&lt;br /&gt;
SELECT to_date(r)&lt;br /&gt;
   FROM foo;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=P%C5%99%C3%ADru%C4%8Dka_SQL/PSM&amp;diff=221</id>
		<title>Příručka SQL/PSM</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=P%C5%99%C3%ADru%C4%8Dka_SQL/PSM&amp;diff=221"/>
		<updated>2007-12-09T09:47:58Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: /* Úvod do SQL/PSM (PL/pgPSM) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Úvod do SQL/PSM (PL/pgPSM)==&lt;br /&gt;
Začátkem devadesátých let minulého století začalo být jasné, že standard ANSI SQL postrádá prostředky pro tvorbu uložených procedur (zejména možnost deklarovat proměnné a dále s nimi pracovat, řízení toku - cykly a podmínky). Na tuto skutečnost reagovaly komerční firmy implementací vlastních proprietárních prostředí. Zřejmě nejpopulárnějšími v té době byly jazyky PL/SQL (Oracle, 1992), T-SQL (Sybase a Microsoft, 1995) a SPL (Informix, 1996). Od roku 1990 se v standardizační komisi ANSI SQl této problematice začala věnovat skupina vývojářů kolem Jima Meltona. Nejstarší zmínka o SQL/PSM je ze srpna roku 1994. (Prvním výstupem bylo PSM-96 (PSM - Persistent Stored Modules) postavené nad SQL92. Ve svém článku [http://www.dbmsmag.com/9701d06.html Budoucnost programování v SQL] z ledna 1997 zmiňuje Joe Celko SQL/PSM jako jazyk vycházející z Algolu rozšířený o prvky Ady (řešení výjimek). V roce 1998 (draft) standard byl již součástí SQL3 a označen současným názvem SQL/PSM (ANSI/ISO/IEC 9075-4:1999). Bohužel v té době většina významných firem již měla své vlastní prostředí (nekompatibilní se standardem), které, jak se ukázalo později, neopustily. Standard byl implementován pouze v těch RDBMS, kde do roku 1998 nebyla možnost tvorby SQL uložených procedur. Kromě DB2 (SQL PL, IBM, 2001) se většinou jedná o minoritní RDNMS: Mimer, Solid, 602SQL server. Do popředí se SQL/PSM opět dostává po roce 2005, kdy je implementován v Advantage Database Serveru (Sybase iAnywhere, 2005), v MySQL (2005) a PostgreSQL (2007). Jen zřídka je SQL/PSM implementován v plném rozsahu. Za zdařilou implementaci se pokládá SQL PL v DB2. Implementace SQL/PSM v PostgreSQL se nazývá (jak je v PostgreSQL zvykem) PL/pgPSM. &lt;br /&gt;
&lt;br /&gt;
Stručně lze programovací jazyk definovaný v SQL/PSM charakterizovat jako jednoúčelový, zcela nový, moderní, jednoduchý procedurální programovací jazyk s integrovaným SQL a úzkou vazbou na prostředí SQL serverů. Jedná se o jazyk s bohatým repertoárem řídících konstrukcí a komfortním modelem zachycení a zpracování chyb. Datové typy, funkce přebírá z hostitelského SQL serveru. I/O rutiny pak úplně chybí (přesně v duchu architektury uložených procedur). Standard SQL/PSM se nevěnuje pouze popisu SQL procedur. Obsahuje i popis uspořádání procedur do modulů a popis tzv. externích procedur (implementace procedur v dalších prg. jazycích, např. C, Cobol, atd). Přestože se jedná o zcela nový programovací jazyk, na celé řadě konstrukcí je zřejmá inspirace jazyky PL/1, jazyky ADA, Modula a dalšími jazyky. &lt;br /&gt;
&lt;br /&gt;
Následující ukázka je modifikací klasického příkladu Hello world v prostředí SQL/PSM. Parametrem funkce je jedinečný identifikátor uživatele. Uvnitř funkce se na základě tohoto identifikátoru získá skutečné jméno a příjmení uživatele z tabulky Users. Všimněte si deklarace proměnné, plnění proměnné hodnotou z tabulky (integrace SQL příkazu SELECT), použití SQL datového typu (varchar) a SQL oparátoru (operátor ||), komentáře.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION hello(uid integer)&lt;br /&gt;
RETURNS varchar AS&lt;br /&gt;
$$&lt;br /&gt;
  BEGIN&lt;br /&gt;
    DECLARE real_name varchar;&lt;br /&gt;
    -- Get real name&lt;br /&gt;
    SET real_name = (SELECT name || &#039; &#039; || surname &lt;br /&gt;
                        FROM Users &lt;br /&gt;
                       WHERE Users.uid = hello.uid);&lt;br /&gt;
    RETURN &#039;Hello, &#039; || real_name;&lt;br /&gt;
  END;&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&lt;br /&gt;
SELECT hello(123);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vložení zdrojového kódu funkce mezi dvojici symbolů $$ je specifické pro PostgreSQL. PL/pgPSM je z pohledu PostgreSQL externí programovací jazyk, jako ostatní podporované PL jazyky (PL/pgSQL, PL/Perl, PL/Python), a tudíž PostgreSQL k tomuto jazyku přistupuje stejně a vynucuje si stejná pravidla zápisu. Implementace SQL/PSM má ještě další dvě podstatné odlišnosti od standardu. Za prvé, PostgreSQL umožňuje definovat pouze funkce (nikoliv procedury), což se projevuje určitými omezeními v řízení transakcí. Za druhé, PostgreSQL implicitně spouští funkce v režimu SECURITY CALLER, kdežto standard předpokládá režim SECURITY DEFINER. Mezi standardem a implementací SQL/PSM v PostgreSQL je ještě několik dalších rozdílů daných určitou volností standardu a implementačními závislostmi, nicméně nejedná se o nijak zvlášť markantní rozdíly. Asi je ještě předčasné počítat s plnou přenositelností kódu mezi RDBMS podporujícími SQL/PSM, nicméně v celé řadě případů sou jsou nutné úpravy minimální. Problémy mohou nastat u normou neošetřených konstrukcí (unbound selects, multirecordsets, atd).&lt;br /&gt;
&lt;br /&gt;
Jazyk SQL/PSM je v RDBMS PostgreSQL (PL/pgPSM) běží na modifikovaném run-time PL/pgSQL. Tudíž výkonnostně i funkčně je na tom velice podobně jako jazyk PL/pgSQL. PL/pgSQL je v tuto chvíli vyzrálejší a časem prověřené prostředí vhodné pro jakkoliv kritické aplikace. PL/pgPSM naopak přináší shodu se standardem a novější, a bohatší programovací jazyk.&lt;br /&gt;
&lt;br /&gt;
Rozlišujeme mezi tzv. externími uloženými procedurami a SQL uloženými procedurami. Rozdíl je v použitém jazyce a v přístupu. Pokud uložené procedury jsou realizovány v klasických prg. jazycích, a běží mimo prostředí SQL serveru (vlastní funkce, vlastní datové typy), jedná se o externí procedury. V případě, že se použije specializovaný jazyk a procedura běží v prostředí SQL serveru (sdílí datové typy a funkce), pak takovou proceduru označujeme jako SQL proceduru. Příklady SQL procedur jsou prostředí PL/SQL, T-SQL, nebo PSQL či PL/pgSQL, PL/pgPSM. Externí procedury jsou standardizované pouze pro jazyk Java (SQL/J). Existuje ovšem celá řada dalších implementací např. CLR pro SQLServer 2005 nebo PL/Perl, PL/Python pro PostgreSQL. Obě třídy procedur mají své pro a proti, doporučuje se ale upřednostnění SQL procedur vyjma těch případů, kdy jsou neefektivní (např. iterační výpočet integrálu) nebo chybí dostatečná funkcionalita (např. I/O operace). Velkou výhodou SQL procedur je integrace s prostředím. Zpravidla nedochází k zbytečným konverzím, a obvykle se používají předzpracované SQL příkazy (prepared statements). Kromě toho všechny statické SQL příkazy jsou verifikované (což ještě neznamená, že jsou 100% správné, nicméně to znamená, že jsou syntakticky správné).&lt;br /&gt;
&lt;br /&gt;
== Praktické tipy návrhu uložených procedur v prostředí PL/pgPSM ==&lt;br /&gt;
V podstatě platí veškerá doporučení pro návrh jakéhokoliv software, tj. požadavky na čitelnost, srozumitelnost kódu:&lt;br /&gt;
* dbejte na dodržování jmenné konvence,&lt;br /&gt;
* používejte komentáře a názorné názvy proměnných,&lt;br /&gt;
* používejte jednotnou konvenci pro odsazování bloků,&lt;br /&gt;
* dodržujte rozumnou délku procedury (kolem 50 řádků),&lt;br /&gt;
* používejte &amp;quot;upovídaný&amp;quot; styl .. využívejte volitelná návěstí smyček.&lt;br /&gt;
&lt;br /&gt;
K tomu ještě specifická doporučení platná pouze pro SQL uložené procedury (bez ohledu na konkrétní prostředí):&lt;br /&gt;
* pomocí prefixů a kvalifikovaných jmen atributů se vyhněte možným kolizím názvů sloupců a jmen proměnných,&lt;br /&gt;
* jednotným způsobem řešte zachycení a ošetření chyb,&lt;br /&gt;
* vyhněte se ISAM programování - co lze vyřešit pomocí SQL řešte pomocí SQL (pozor na cykly napříč celými tabulkami),&lt;br /&gt;
* omezte kurzory a dočasné tabulky na nezbytné minimum,&lt;br /&gt;
* v triggerech neopravujte data,&lt;br /&gt;
* procedury by měli realizovat určitou činnost, nikoliv jen zapouzdřit selecty.&lt;br /&gt;
&lt;br /&gt;
Na následujících dvou příkladech si všimněte špatného (ISAM) přístupu a dobrého přístupu k návrhu uložených procedur. Obě procedury volají pro určitou podmnožinu zaměstnanců proceduru print_info. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- spatne (ISAM pristup)&lt;br /&gt;
CREATE OR REPLACE FUNCTION report_a()&lt;br /&gt;
RETURNS void AS &lt;br /&gt;
$$&lt;br /&gt;
  main: FOR outer AS &lt;br /&gt;
            SELECT id FROM Users &lt;br /&gt;
        DO&lt;br /&gt;
          BEGIN&lt;br /&gt;
            DECLARE name, surname varchar;&lt;br /&gt;
            DECLARE age int;&lt;br /&gt;
            SET (name, surname, age) = (SELECT e.name, e.surname, e.age&lt;br /&gt;
                                           FROM Employers e&lt;br /&gt;
                                          WHERE e.id = outer.id);&lt;br /&gt;
            IF age &amp;gt;= 20 AND age &amp;lt;= 29 THEN&lt;br /&gt;
              CALL print_info(name, surname, age, &#039;y&#039;);&lt;br /&gt;
            ELSE IF age &amp;gt;=30 AND age &amp;lt;= 50 THEN&lt;br /&gt;
              CALL print_info(name, surname, age, &#039;o&#039;);&lt;br /&gt;
            END IF;                     &lt;br /&gt;
          END;&lt;br /&gt;
        END FOR main;&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&lt;br /&gt;
-- dobre&lt;br /&gt;
CREATE OR REPLACE FUNCTION report_b()&lt;br /&gt;
RETURNS void AS&lt;br /&gt;
$$&lt;br /&gt;
  main: FOR fc AS&lt;br /&gt;
            -- veskere mozne podminky a transformace resim v prikazu SELECT&lt;br /&gt;
            SELECT e.*, CASE WHEN e.age BETWEEN 20 AND 29 THEN &#039;y&#039;&lt;br /&gt;
                             WHEN e.age BETWEEN 30 AND 50 THEN &#039;o&#039; END AS tp&lt;br /&gt;
               FROM Employers e&lt;br /&gt;
              WHERE e.age BETWEEN 20 AND 50&lt;br /&gt;
        DO&lt;br /&gt;
          CALL print_info(fc.name, fc.surname, fc.age, fc.tp);&lt;br /&gt;
        END FOR main;&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Příručka jazyka PL/pgPSM ==&lt;br /&gt;
Každá funkce obsahuje jeden PL/pgPSM příkaz. Následující jedno příkazové funkce jsou korektní. Pozn. Pro uživatele PL/SQL jazyků to může být mírný šok. Na rozíl od PL/SQL, kde základním příkazem funkce nebo procedury je složený příkaz, je v SQL/PSM základním příkazem libovolný příkaz. Pokud funkce neobsahuje žádný SQL příkaz, používejte atribut IMMUTABLE. Kromě jiného i zajistíte efektivnější provádění funkce. Speciálním PL/pgPSM příkazem je [[Složený příkaz|složený příkaz]], který umožňuje zadat libovolně dlouhou syntakticky správnou posloupnost PL/pgPSM příkazů.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION sum2params(IN a integer, IN b integer, OUT c integer) AS &lt;br /&gt;
$$&lt;br /&gt;
  SET c = a + b;&lt;br /&gt;
$$ LANGUAGE plpgpsm IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION insert_val(a integer)&lt;br /&gt;
RETURNS void AS&lt;br /&gt;
$$&lt;br /&gt;
  INSERT INTO Foo VALUES(a);&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION get_sum(IN a integer, OUT b integer) AS&lt;br /&gt;
$$&lt;br /&gt;
  SET b = (SELECT sum(f.a) &lt;br /&gt;
              FROM Foo f&lt;br /&gt;
             WHERE f.a &amp;gt; get_sum.a);&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION dummy() &lt;br /&gt;
RETURNS void AS&lt;br /&gt;
$$&lt;br /&gt;
  BEGIN&lt;br /&gt;
  END;&lt;br /&gt;
$$ LANGUAGE plpgpsm IMMUTABLE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
K dispozici jsou následující příkazy:&lt;br /&gt;
* [[SQL příkaz#SQL příkaz|SQL příkaz]],&lt;br /&gt;
* [[Složený příkaz|složený příkaz]] -  mezi dvojicí BEGIN a END je libovolný počet PL/pgPSM příkazů oddělených středníkem,&lt;br /&gt;
* [[Příkazy CALL a RETURN|Volání a ukončení procedury]] - příkazy CALL a RETURN,&lt;br /&gt;
* [[Přiřazovací příkaz|přiřazovací příkaz]] - SET proměnná = hodnota,&lt;br /&gt;
* [[Podmíněné provádění příkazů]]:&lt;br /&gt;
** [[Podmíněné provádění příkazů#Příkaz IF|IF ELSEIF ELSE END IF]],&lt;br /&gt;
** [[Podmíněné provádění příkazů#Příkaz CASE|CASE WHEN THEN END CASE]].&lt;br /&gt;
* [[Příkazy cyklu|Příkazy cyklu]]&lt;br /&gt;
** [[Příkazy cyklu#Příkaz LOOP|LOOP co END LOOP]],&lt;br /&gt;
** [[Příkazy cyklu#Příkaz WHILE|WHILE podmínka DO co END WHILE]],&lt;br /&gt;
** [[Příkazy cyklu#Příkaz REPEAT|REPEAT co UNTIL podmínka END REPEAT]],&lt;br /&gt;
** [[Příkazy cyklu#Příkaz FOR|FOR select DO co END FOR]] (iterace napříč výsledkem dotazu).&lt;br /&gt;
* [[signalizace chyb]] - SIGNAL a RESIGNAL&lt;br /&gt;
* [[Příkaz PRINT|zobrazení výsledku na konzoli]] - PRINT (nestandardní konstrukce),&lt;br /&gt;
* [[Příkazy cyklu#Příkazy LEAVE a ITERATE|opuštění a nová iterace cyklu]] - příkazy LEAVE a ITERATE,&lt;br /&gt;
* [[Dynamické SQL|dynamické SQL]] - EXECUTE a EXECUTE IMMEDIATE,&lt;br /&gt;
* [[Příkaz GET DIAGNOSTICS]] - získání diagnostických údajů.&lt;br /&gt;
&lt;br /&gt;
Ještě před samotným popisem PL/pgPSM příkazů si projděte následující jednoduché ukázky:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- case statement&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo1(a integer)&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
  CASE a&lt;br /&gt;
    WHEN 1, 3, 5, 7, 9 THEN&lt;br /&gt;
      PRINT a, &#039;is odd number&#039;;&lt;br /&gt;
    WHEN 2, 4, 6, 8, 10 THEN&lt;br /&gt;
      PRINT a. &#039;is odd number&#039;;&lt;br /&gt;
    ELSE &lt;br /&gt;
      PRINT a, &#039;isn&#039;t from range 1..10&#039;;&lt;br /&gt;
  END CASE;&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&lt;br /&gt;
-- while statement&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo2(a integer)&lt;br /&gt;
RETURNS void AS &lt;br /&gt;
$$&lt;br /&gt;
  BEGIN&lt;br /&gt;
    DECLARE i integer DEFAULT 1;&lt;br /&gt;
    WHILE i &amp;lt;= a &lt;br /&gt;
    DO&lt;br /&gt;
      PRINT i;&lt;br /&gt;
      SET i = i + 1;&lt;br /&gt;
    END WHILE;&lt;br /&gt;
  END&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&lt;br /&gt;
-- for statement&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo3(a integer)&lt;br /&gt;
RETURNS void AS &lt;br /&gt;
$$&lt;br /&gt;
  FOR fc AS&lt;br /&gt;
      SELECT i &lt;br /&gt;
         FROM generate_series(1,a) AS g(i)&lt;br /&gt;
  DO&lt;br /&gt;
    PRINT fc.i;&lt;br /&gt;
  END FOR;&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
=== Použití jazyka PL/pgPSM ===&lt;br /&gt;
* [[SQL příkaz#Použití kurzorů|Použití kurzorů]]&lt;br /&gt;
* [[SQL příkaz#Funkce vracející tabulky|Funkce vracející tabulky]]&lt;br /&gt;
* [[Složený příkaz#Ošetření chyb|Ošetření chyb, signalizace chyby (výjimky) a zachycení signálu]]&lt;br /&gt;
* [[Použití dočasných tabulek v PL/pgPSM]]&lt;br /&gt;
&lt;br /&gt;
== Parametry ovlivňující provádění funkcí ==&lt;br /&gt;
V PL/pgPSM jsou k dispozici dva přepínače: DUMP a RECOMPILE. Použití prvního způsobí výpis přeloženého kódu funkce do systémového logu. Druhý zajistí vynulování nakešovaných prováděcích plánů při každém startu funkce. Parametry se zapisují ještě před vlastní kód za klíčové slovo #OPTION.&lt;br /&gt;
&lt;br /&gt;
=== #OPTION DUMP ===&lt;br /&gt;
Účelem tohoto přepínače je zobrazení přeloženého kódu do systémováho logu. Rozhodně se nejedná o typickou činnost - většina uživatelů PL/pgSQL o této možnosti v životě neslyšela. Dokáže být velice užitečný při hledání kolize názvů proměnných a SQL atributů. V případě této chyby nám systém hlásí (v lepším případě), že dotaz nelze přeložit nebo (v horším případě) je výsledek dotazu jiný než očekáváme.&lt;br /&gt;
&lt;br /&gt;
Tento přepínač demonstruje skutečnost, že PL/pgSQL je v podstatě preprocesor jazyka SQL. Vlastní interpret je natolik minimalistický, že pro provádění veškerých operací (logických, aritmetických) se spoléhá na SQL. Díky tomu je zajištěna kompatibilita s SQL. Na druhou stranu PL/pgSQL se nehodí pro výpočetně náročné úlohy (s provedením každého SQL příkazu je spojená nezanedbatelná režie). K těmto účelům v PostgreSQL slouží PL/Perl, PL/Python nebo klasické C.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- tabulka foo obsahuje sloupec a&lt;br /&gt;
CREATE OR REPLACE FUNCTION kolize(a integer)&lt;br /&gt;
RETURNS SETOF Foo AS&lt;br /&gt;
$$&lt;br /&gt;
#option dump&lt;br /&gt;
  SELECT * &lt;br /&gt;
    FROM Foo &lt;br /&gt;
   WHERE a = a;&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jedná se o skutečně záludnout chybu (a poměrně častou). Přeložený kód výpadá následovně:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Execution tree of successfully compiled PL/pgSQL function kolize(integer):                            &lt;br /&gt;
                                                                                                      &lt;br /&gt;
Function&#039;s data area:                                                                                 &lt;br /&gt;
    entry 0: VAR $1               type int4 (typoid 23) atttypmod -1                                  &lt;br /&gt;
    entry 1: VAR found            type bool (typoid 16) atttypmod -1                                  &lt;br /&gt;
                                                                                                      &lt;br /&gt;
Function&#039;s statements:                                                                                &lt;br /&gt;
  0: *unnamed*:                                                                                       &lt;br /&gt;
     BLOCK                                                                                            &lt;br /&gt;
  2:   SQL &#039;SELECT * FROM Foo WHERE  $1  =  $1  {$1=0}&#039;                                               &lt;br /&gt;
  0:   RETURN NULL                                                                                    &lt;br /&gt;
     END *unnamed*                                                                                    &lt;br /&gt;
                                                                                                      &lt;br /&gt;
End of execution tree of function kolize(integer)   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Chyba je v podmínce WHERE. PL/pgSQL nechápe zápis a = a jako podmínku typu: SQL atribut je roven proměnné. Zápis znamená, že proměnná a je rovna proměnné a, což je splněno pro všechny řádky tabulky, a také výsledkem je celá tabulka Foo.&lt;br /&gt;
&lt;br /&gt;
=== #OPTION RECOMPILE ===&lt;br /&gt;
Tento přepínač snižuje pravděpodobnost nekonzistence prováděcích plánů tím, že před každým startem procesury se procedura znovu přeloží a tím dojde k vyčištění cache prováděcích plánů. Zároveň se tím ale prodlouží doba spuštění procedury (dochází k překladu) a doba běhu (dochází k průbežnému vytváření prováděcích plánů). Tudíž tento přepínač používejte jen v nutných případech, nebo ještě lépe, nepoužívejte jej vůbec. Jak se vyhnout použití přepínače RECOMPILE je popsáno v sekci věnované [[Použití dočasných tabulek v PL/pgPSM|dočasným tabulkám]].Účelem tohoto přepínače je usnadnit portování uložených procedur z jiných RDBMS, kde jinak funguje ukládání prováděcích plánů. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION testr()&lt;br /&gt;
RETURNS SETOF Foo AS&lt;br /&gt;
$$&lt;br /&gt;
#option dump recompile&lt;br /&gt;
  BEGIN&lt;br /&gt;
    DROP TABLE IF EXISTS FooG;&lt;br /&gt;
    INSERT INTO FooG&lt;br /&gt;
       SELECT * &lt;br /&gt;
          FROM Foo;&lt;br /&gt;
    RETURN TABLE(SELECT *&lt;br /&gt;
                    FROM FooG);&lt;br /&gt;
  END;&lt;br /&gt;
$$ LANGUAGE plpgpsm;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Tento přepínač je analogií přepínači WITH RECOMPILE v T-SQL Microsoft SQL Serveru.&lt;br /&gt;
&lt;br /&gt;
== Portování uložených procedur z MySQL 5.x ==&lt;br /&gt;
Originální [[zdrojový kód procedury generující fraktály]] je přebrán z archivu MySQL dev. PostgreSQL řeší jinak:&lt;br /&gt;
* změnu aktuálního schématu,&lt;br /&gt;
* zápis zdrojového kódu procedury (nepoužívá mechanismus separátoru),&lt;br /&gt;
* pro spojení řetězců používá operátor || nikoliv funkci concat,&lt;br /&gt;
* neumožňuje volné SQL dotazy, jelikož nepodporuje procedury, ale pouze funkce.&lt;br /&gt;
&lt;br /&gt;
Všimněte si minimálních rozdílů v [[zdrojový kód procedury generující fraktál pro PostgreSQL|upraveném kódu pro PostgreSQL]].&lt;br /&gt;
&lt;br /&gt;
== Instalace PL/pgPSM ==&lt;br /&gt;
Vzhledem k nezralosti implementace PL/pgPSM ještě tento interpret není zařazen do distribuce. Zatím je ke stažení v jeslích postgresql projektů http://pgfoundry. Přesun do distribuce bude možný až po plné implementaci standardu a po důkladném otestování. Zatím zbývá dopsat podporu příkazů RESIGNAL a GET STACKET DIAGNOSTIC. Pokud se obejdete bez těchto příkazů, můžete používat PL/pgPSM bez obav. Interpret PL/pgPSM je modifikací interpretu PL/pgSQL, který je lety i tisíci projekty důkladně prověřen.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Pro překlad PL/pgPSM je potřeba instalovat PostgreSQL ze zdrojových kódů.&#039;&#039; Potřebujete alespoň verzi 8.2. Poslední verzi PL/pgPSM naleznete na http://pgfoundry.org/frs/?group_id=1000238&amp;amp;release_id=767.&lt;br /&gt;
=== Postup ===&lt;br /&gt;
* Stáhněte si nejnovější zdrojové soubory PL/pgPSM z adresáře http://pgfoundry.org/frs/?group_id=1000238&lt;br /&gt;
* pokud máte PostgreSQL instalovaný ze zdrojových kódů, rozbalte archív do adresáře contrib&lt;br /&gt;
* proveďte příkazy make a make install&lt;br /&gt;
* jako superuser spusťe psql postgres &amp;lt; plpgpsm.sql&lt;br /&gt;
* od tohoto okamžiku můžete používat plpgpsm stejně jako ostatní jazyky&lt;br /&gt;
* test instalace make installcheck&lt;br /&gt;
&lt;br /&gt;
Pokud nemáte PostgreSQL instalovaný ze zdrojových kódů, musíte mít alespoň develop knihovny PostgreSQL&lt;br /&gt;
* kdekoliv rozbalte archiv plpgpsm&lt;br /&gt;
* spusťte příkazy make USE_PGXS=1 a make USE_PGXS=1 install&lt;br /&gt;
* další postup je stejný jako v předchozím případě&lt;br /&gt;
&lt;br /&gt;
Uvítáme jakoukoliv formu spolupráce. A to ať doplněním této dokumentace, její korekturou, překladem, rozšířením testovacích scénářů, doplněním funkcionality nebo samotným použitím.&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
	<entry>
		<id>http://postgres.cz/index.php?title=Ke%C5%A1ov%C3%A1n%C3%AD_v%C3%BDsledku_funkc%C3%AD_v_PL/Perl&amp;diff=363</id>
		<title>Kešování výsledku funkcí v PL/Perl</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Ke%C5%A1ov%C3%A1n%C3%AD_v%C3%BDsledku_funkc%C3%AD_v_PL/Perl&amp;diff=363"/>
		<updated>2007-10-30T21:46:27Z</updated>

		<summary type="html">&lt;p&gt;194.255.108.253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Žádné jiné aplikace nedokáží vygenerovat tak ohromnou zátěž jako www aplikace. Je to dáno jednak vlastnostmi protokolu HTTP, jednak dostupností www aplikací. Neoptimálně napsané aplikace dokáží položit libovolnou databázi na libovolném hardware. Naopak dobře navržené aplikace si vystačí i s levným a na slušném vybavení poskytují dostatečnou rezervu výkonu jako ochrany před špičkovým zatížením. V prvé řadě jde o minimalizaci počtu dotazů do databáze. Zejména těch pomalých (to jsou už dotazy nad 50ms). Řešením je intenzivní využívání cache. Všechno co lze, je třeba umístit do cache. Příklad z praxe. Prodejní web zaměřený na literaturu obsahoval na každé stránce žebříček nejprodávanějších knih. Bez použití cache se při každm vykreslení stránky pustil dotaz obsahující sekvenční čtení tabulek prodeje a knih doplnění požadavkem na třídění. Tento relativně jednoduchý dotaz (trvající cca 20ms) v reálném provozu dokonale vyřadil celý server z provozu. V jednu chvíli jej spouštělo 500-1000 uživatelů. Proto se používají techniky, které uživatele do jisté míry šidí. Jen ve výjimečných případech musí všichni uživatelé vidět aktuální data. Zrovna u žebříčku prodejnosti bohatě stačí aktualizovat data několikrát denně. Pětková verze PHP obsahují nástroje, kterými lze vybudovat a udržovat datovou cache. Ty se použily také v případě elektronického knihkupectví s celkem očekávaným efektem. Místo nejnovějšího hw se mohl bezproblémově použít několik let starý server.&lt;br /&gt;
&lt;br /&gt;
Datové cache můžeme používat i na databázové úrovni. První obvyklé řešení jsou materializované pohledy aktualizované triggery. Jedná se o relativně jednoduché řešení, které udržuje agregovaná data 100% aktuální. To relativně je ovšem na místě. Napsat opravdu robustní řešení není až tak snadné, a k tomu ještě v řadě případů potřebujeme explicitní zamykání, které se negativně projeví na průchodnosti (výkonu) databáze. Pokud nepotřebujeme vždy aktuální data, tak nejjednodušším řešením jsou materializované pohledy aktualizované cronem. Nevýhodou tohoto řešení je závislost na externí službě (cron). Při jejím selhání budou materializované pohledy neaktuální (nad rámec předpokládané neaktuálnosti). Tuto nevýhodu nemá použití cache na výstup z uložené procedury. &lt;br /&gt;
&lt;br /&gt;
V PostgreSQL lze použít cache v prostředí PL/Perl, kde je k dispozici pole $_SHARED, kam můžeme ukládat libovolné hodnoty (i tabulky). Uložit tabulku do této cache mne napadlo až poté, co jsem viděl [http://www.oracle.com/technology/oramag/oracle/07-sep/o57plsql.html  cache v Oracle 11g], která je ovšem mnohem sofistikovanější, bezpečnější a efektivnější (znevalidní obsah cache při změně dat, lze nastavit limity pro cache, cache je sdílena všemi uživateli). Cache PL/Perlu je omezena na session - což by nemělo vadit, pokud používáte nějakou techniku poolování. Data v PL/Perlu nejsou uložena příliš efektivně, takže do cache není vhodné  ukládat velké tabulky. &lt;br /&gt;
&lt;br /&gt;
Následující příklady počítají s následujícím datovým modelem:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE Books(&lt;br /&gt;
  id serial PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(20));&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE Sale(&lt;br /&gt;
  book_id integer REFERENCES Books(id),&lt;br /&gt;
  inserted timestamp DEFAULT(CURRENT_TIMESTAMP)&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
INSERT INTO Books VALUES(1,&#039;Dracula&#039;);&lt;br /&gt;
INSERT INTO Books VALUES(2,&#039;Nosferatu&#039;);&lt;br /&gt;
INSERT INTO Books VALUES(3,&#039;Bacula&#039;);&lt;br /&gt;
&lt;br /&gt;
INSERT INTO Sale VALUES(1, &#039;2007-10-11&#039;);&lt;br /&gt;
INSERT INTO Sale VALUES(2, &#039;2007-10-12&#039;);&lt;br /&gt;
INSERT INTO Sale VALUES(2, &#039;2007-10-13&#039;);&lt;br /&gt;
INSERT INTO Sale VALUES(3, &#039;2007-10-10&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Funkce Top10Books vrátí tabulku 10 nejprodávanějších knih v daném měsíci. Řádky jsou očíslované. Je to triviální funkce v PL/pgSQL. Na malé testovací množině trvá cca 2ms. Pokud ale testovací množina obsahovala dvacet tisíc prodejů během jednoho měsíce, to je docela realistické, tak její provedení trvá cca 120ms. Což už je příliš, pokud by se měla volat, při vykreslení každé stránky. Výsledkem je malá tabulka (obsahuje 2 sloupce, max 10 řádek), tudíž je ideální kandidát pro kešování.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- Top10 is on every page                                                                                                                                                                                                        &lt;br /&gt;
CREATE OR REPLACE FUNCTION Top10Books(IN date, OUT ordr integer, OUT name varchar(20))&lt;br /&gt;
RETURNS SETOF RECORD&lt;br /&gt;
AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  ordr := 0;&lt;br /&gt;
  FOR name IN SELECT b.name&lt;br /&gt;
                  FROM Books b&lt;br /&gt;
                       JOIN&lt;br /&gt;
                       Sale s&lt;br /&gt;
                       ON b.id = s.book_id&lt;br /&gt;
                 WHERE s.inserted BETWEEN date_trunc(&#039;month&#039;, $1)&lt;br /&gt;
                                      AND date_trunc(&#039;month&#039;, $1) + interval &#039;1month&#039; - interval &#039;1day&#039;&lt;br /&gt;
                 GROUP BY b.name&lt;br /&gt;
                 ORDER BY count(*) DESC&lt;br /&gt;
                 LIMIT 10&lt;br /&gt;
  LOOP&lt;br /&gt;
    ordr := ordr + 1;&lt;br /&gt;
    RETURN NEXT;&lt;br /&gt;
  END LOOP;&lt;br /&gt;
  RETURN;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Přepsáním do perlu nic nezískáme. Funkce trvá stejně dlouho. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION Top10BooksPerl(IN date, OUT ordr integer, OUT name varchar(20))&lt;br /&gt;
RETURNS SETOF RECORD&lt;br /&gt;
AS $$&lt;br /&gt;
    if (not defined($_SHARED{plan_for_top10books}))&lt;br /&gt;
    {&lt;br /&gt;
       $_SHARED{plan_for_top10books} = spi_prepare(&lt;br /&gt;
               &#039;SELECT b.name                                                                                                                                                                                                    &lt;br /&gt;
                  FROM Books b                                                                                                                                                                                                   &lt;br /&gt;
                       JOIN                                                                                                                                                                                                      &lt;br /&gt;
                       Sale s                                                                                                                                                                                                    &lt;br /&gt;
                       ON b.id = s.book_id                                                                                                                                                                                       &lt;br /&gt;
                 WHERE s.inserted BETWEEN date_trunc(\&#039;month\&#039;, $1)                                                                                                                                                              &lt;br /&gt;
                                      AND date_trunc(\&#039;month\&#039;, $1) + interval \&#039;1month\&#039; - interval \&#039;1day\&#039;                                                                                                                    &lt;br /&gt;
                 GROUP BY b.name                                                                                                                                                                                                 &lt;br /&gt;
                 ORDER BY count(*) DESC                                                                                                                                                                                          &lt;br /&gt;
                 LIMIT 10&#039; , &#039;DATE&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    my $row;&lt;br /&gt;
    my $i = 0;&lt;br /&gt;
    my $sth = spi_query_prepared($_SHARED{plan_for_top10books}, $_[0]);&lt;br /&gt;
    while (defined ($row = spi_fetchrow($sth))) {&lt;br /&gt;
        return_next({&lt;br /&gt;
            ordr =&amp;gt; ++$i,&lt;br /&gt;
            name =&amp;gt; $row-&amp;gt;{name}&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
    return;&lt;br /&gt;
$$ LANGUAGE plperlu;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Teprve s využitím cache získáme zásadní urychlení - očekávané. Funkce má druhý IN parametr, který určuje, zda-li cache má být aktualizována, nebo zda-i se má použít obsah cache. Díky použití cache není žádny rozdíl v rychlosti mezi voláním funkce s malou testovací množinou nebo s velkou testovací množinou.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION Top10BooksCached(IN date, IN bool, OUT ordr integer, OUT name varchar(20))&lt;br /&gt;
RETURNS SETOF RECORD&lt;br /&gt;
AS $$&lt;br /&gt;
    return $_SHARED{tableof_top10book}&lt;br /&gt;
        if (defined ($_SHARED{tableof_top10book}) and not (defined($_[1]) and $_[1] eq &amp;quot;t&amp;quot;));&lt;br /&gt;
    if (not defined($_SHARED{plan_for_top10books}))&lt;br /&gt;
    {&lt;br /&gt;
       $_SHARED{plan_for_top10books} = spi_prepare(&lt;br /&gt;
               &#039;SELECT b.name                                                                                                                                                                                                    &lt;br /&gt;
                  FROM Books b                                                                                                                                                                                                   &lt;br /&gt;
                       JOIN                                                                                                                                                                                                      &lt;br /&gt;
                       Sale s                                                                                                                                                                                                    &lt;br /&gt;
                       ON b.id = s.book_id                                                                                                                                                                                       &lt;br /&gt;
                 WHERE s.inserted BETWEEN date_trunc(\&#039;month\&#039;, $1)                                                                                                                                                              &lt;br /&gt;
                                      AND date_trunc(\&#039;month\&#039;, $1) + interval \&#039;1month\&#039; - interval \&#039;1day\&#039;                                                                                                                    &lt;br /&gt;
                 GROUP BY b.name                                                                                                                                                                                                 &lt;br /&gt;
                 ORDER BY count(*) DESC                                                                                                                                                                                          &lt;br /&gt;
                 LIMIT 10&#039; , &#039;DATE&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    my $row;&lt;br /&gt;
    my $i = 0;&lt;br /&gt;
    my $heap;&lt;br /&gt;
    my $sth = spi_query_prepared($_SHARED{plan_for_top10books}, $_[0]);&lt;br /&gt;
    while (defined ($row = spi_fetchrow($sth))) {&lt;br /&gt;
        push @$heap, {ordr =&amp;gt; ++$i, name =&amp;gt; $row-&amp;gt;{name}}&lt;br /&gt;
    }&lt;br /&gt;
    $_SHARED{tableof_top10book} =  $heap ;&lt;br /&gt;
    return $_SHARED{tableof_top10book};&lt;br /&gt;
$$ LANGUAGE plperlu;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Modifikací předešlého, je funkce s cache omezenou časem. Např. budu chtít, aby se cache aktualizovala vždy po pěti minutách (300sec).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION Top10BooksCached(IN date, IN integer, OUT ordr integer, OUT name varchar(20))&lt;br /&gt;
RETURNS SETOF RECORD&lt;br /&gt;
AS $$&lt;br /&gt;
    return $_SHARED{tableof_top10book}&lt;br /&gt;
        if (defined ($_SHARED{tableof_top10book}) &lt;br /&gt;
                and defined($_SHARED{actualised_top10book}) &lt;br /&gt;
                and ($_SHARED{actualised_top10book} + $_[1] &amp;gt; time));&lt;br /&gt;
    if (not defined($_SHARED{plan_for_top10books}))&lt;br /&gt;
    {&lt;br /&gt;
       $_SHARED{plan_for_top10books} = spi_prepare(&lt;br /&gt;
               &#039;SELECT b.name                                                                                                                                                                                                    &lt;br /&gt;
                  FROM Books b                                                                                                                                                                                                   &lt;br /&gt;
                       JOIN                                                                                                                                                                                                      &lt;br /&gt;
                       Sale s                                                                                                                                                                                                    &lt;br /&gt;
                       ON b.id = s.book_id                                                                                                                                                                                       &lt;br /&gt;
                 WHERE s.inserted BETWEEN date_trunc(\&#039;month\&#039;, $1)                                                                                                                                                              &lt;br /&gt;
                                      AND date_trunc(\&#039;month\&#039;, $1) + interval \&#039;1month\&#039; - interval \&#039;1day\&#039;                                                                                                                    &lt;br /&gt;
                 GROUP BY b.name                                                                                                                                                                                                 &lt;br /&gt;
                 ORDER BY count(*) DESC                                                                                                                                                                                          &lt;br /&gt;
                 LIMIT 10&#039; , &#039;DATE&#039;);&lt;br /&gt;
    }&lt;br /&gt;
    my $row;&lt;br /&gt;
    my $i = 0;&lt;br /&gt;
    my $heap;&lt;br /&gt;
    my $sth = spi_query_prepared($_SHARED{plan_for_top10books}, $_[0]);&lt;br /&gt;
    while (defined ($row = spi_fetchrow($sth))) {&lt;br /&gt;
        push @$heap, {ordr =&amp;gt; ++$i, name =&amp;gt; $row-&amp;gt;{name}}&lt;br /&gt;
    }&lt;br /&gt;
    $_SHARED{tableof_top10book} =  $heap ;&lt;br /&gt;
    $_SHARED{actualised_top10book} = time;&lt;br /&gt;
    return $_SHARED{tableof_top10book};&lt;br /&gt;
$$ LANGUAGE plperlu;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Použití:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# select * from Top10BooksCached(current_date, 300);&lt;br /&gt;
 ordr |   name&lt;br /&gt;
------+-----------&lt;br /&gt;
    1 | Nosferatu&lt;br /&gt;
    2 | Bacula&lt;br /&gt;
    3 | Dracula&lt;br /&gt;
(3 rows)&lt;br /&gt;
&lt;br /&gt;
Time: 128,965 ms&lt;br /&gt;
postgres=# select * from Top10BooksCached(current_date, 300);&lt;br /&gt;
 ordr |   name&lt;br /&gt;
------+-----------&lt;br /&gt;
    1 | Nosferatu&lt;br /&gt;
    2 | Bacula&lt;br /&gt;
    3 | Dracula&lt;br /&gt;
(3 rows)&lt;br /&gt;
&lt;br /&gt;
Time: 11,911 ms&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>194.255.108.253</name></author>
	</entry>
</feed>