<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>radek.cc &#187; postgresql</title>
	<atom:link href="http://radek.cc/tag/postgresql/feed/" rel="self" type="application/rss+xml" />
	<link>http://radek.cc</link>
	<description>Radosław Zieliński</description>
	<lastBuildDate>Fri, 25 Jun 2010 05:22:08 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>~/.psqlrc tricks: missing and unused indexes</title>
		<link>http://radek.cc/2009/09/05/psqlrc-tricks-indexes/</link>
		<comments>http://radek.cc/2009/09/05/psqlrc-tricks-indexes/#comments</comments>
		<pubDate>Sat, 05 Sep 2009 15:16:57 +0000</pubDate>
		<dc:creator>radek</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[macros]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[psql]]></category>

		<guid isPermaLink="false">http://radek.cc/?p=156</guid>
		<description><![CDATA[psql macros for finding missing and unused indexes.]]></description>
			<content:encoded><![CDATA[<p>Two more <a href="http://radek.cc/tag/psql+macros/">~/.psqlrc tricks</a> with extra long lines:</p>
<pre style="padding-left: 30px; white-space: pre-wrap;"><span style="text-decoration: underline;">\set trashindexes</span> '( select s.schemaname as sch, s.relname as rel, s.indexrelname as idx, s.idx_scan as scans, pg_size_pretty(pg_relation_size(s.relid)) as ts, pg_size_pretty(pg_relation_size(s.indexrelid)) as "is" from pg_stat_user_indexes s join pg_index i on i.indexrelid=s.indexrelid left join pg_constraint c on i.indrelid=c.conrelid and array_to_string(i.indkey, '' '') = array_to_string(c.conkey, '' '') where i.indisunique is false and pg_relation_size(s.relid) &gt; 1000000 and s.idx_scan &lt; 100000 and c.confrelid is null order by s.idx_scan asc, pg_relation_size(s.relid) desc )'
<span style="text-decoration: underline;">\set missingindexes</span> '( select src_table, dst_table, fk_name, pg_size_pretty(s_size) as s_size, pg_size_pretty(d_size) as d_size, d from ( select distinct on (1,2,3,4,5) textin(regclassout(c.conrelid)) as src_table, textin(regclassout(c.confrelid)) as dst_table, c.conname as fk_name, pg_relation_size(c.conrelid) as s_size, pg_relation_size(c.confrelid) as d_size, array_upper(di.indkey::int[], 1) + 1 - array_upper(c.conkey::int[], 1) as d from pg_constraint c left join pg_index di on di.indrelid = c.conrelid and array_to_string(di.indkey, '' '') ~ (''^'' || array_to_string(c.conkey, '' '') || ''( |$)'') join pg_stat_user_tables st on st.relid = c.conrelid where c.contype = ''f'' order by 1,2,3,4,5,6 asc) mfk where mfk.d is distinct from 0 and mfk.s_size &gt; 1000000 order by mfk.s_size desc, mfk.d desc )'</pre>
<p>The first one displays <strong>unused indexes</strong>.  Why would you care?</p>
<ol>
<li>wasted RAM and disk space,</li>
<li>longer restore,</li>
<li>slower INSERT, UPDATE and DELETE statements.</li>
</ol>
<p>Running this is useless after a fresh restore (or after calling <code><a href="http://www.postgresql.org/docs/current/static/monitoring-stats.html">pg_stat_reset</a>(), </code>or &#8212; in case you&#8217;re running something ancient, like 8.2 or older, with <code><a href="http://www.postgresql.org/docs/8.2/static/runtime-config-statistics.html#GUC-STATS-RESET-ON-SERVER-START">stats_reset_on_server_start</a>=on</code> &#8212; after restarting the server).</p>
<p>It excludes unique indexes and the ones on columns being foreign keys (used when deleting / updating rows in the tables the FK points to).</p>
<p>Tables smaller than 1MB are excluded as well.</p>
<p>Example usage:</p>
<pre style="padding-left: 30px;">radek=# :trashindexes limit 5;
  sch   |  rel   |            idx            | scans |   ts    |   is
--------+--------+---------------------------+-------+---------+---------
 public | tracks | i_tracks_theme            |     0 | 15 MB   | 4664 kB
 public | tracks | i_tracks_album_theme      |     0 | 15 MB   | 9696 kB
 public | tracks | i_tracks_theme_album      |     0 | 15 MB   | 6616 kB
 public | albums | i_albums_theme            |     0 | 5296 kB | 1336 kB
 public | albums | i_albums_collection_theme |     0 | 5296 kB | 1664 kB
 public | albums | i_albums_theme_collection |     0 | 5296 kB | 1664 kB
(5 rows</pre>
<p>Columns: schema name, table name, index name, number of index scans, table size, index size.</p>
<p>The second one suggests <strong>missing indexes</strong>.</p>
<p>Imagine a large table (large enough that you wouldn&#8217;t like any <a href="http://www.postgresql.org/docs/current/static/using-explain.html">sequential scans</a> on it) with a <a href="http://www.postgresql.org/docs/current/static/tutorial-fk.html">foreign key</a> to another table.  Now, when you DELETE a referenced row from the second table, Postgres has to enforce the constraint somehow (raise an exception, delete the row from the first table or set the FK column(s) to <code>NULL</code>).  For that, the first table has to be scanned somehow, to find out if any action is needed.</p>
<p>It&#8217;s not worth it if the referencing table is small, or if it&#8217;s not-so-small but always in RAM, or if the rows in the second table are never updated or deleted.  The query omits small tables using an arbitrary number; tweak for your needs.</p>
<p>Example usage:</p>
<pre style="padding-left: 30px;">msawi11=# set search_path to "$user"; -- demonstrate lack of separate columns for schemas
SET
msawi11=# select * from :missingindexes x where src_table ~ 'promoc|usl';
    src_table    |     dst_table     |         fk_name         | s_size  |   d_size   | d
-----------------+-------------------+-------------------------+---------+------------+---
 public.promocje | public.operatorzy | promocje_fk_operator_id | 4736 kB | 8192 bytes |
 public.uslugi   | public.taryfy     | uslugi_fk_taryfa_id     | 1032 kB | 552 kB     | 2
(2 rows</pre>
<p>Last three columns: source table size, destination table size, distance.  If &#8220;distance&#8221; is not null, it means there already is an index starting with the FK columns; &#8220;distance&#8221; constains the number of extra columns.</p>
<p>In this example, &#8220;uslugi.taryfa_id&#8221; is a FK to the &#8220;taryfy&#8221; table.  There is an unique index <code>on uslugi (taryfa_id, foo, bar)</code>, so the distance = 2.</p>
<p>The textin(regclassout(oid)) calls are there for Pg 8.2 compatibility (lacks a regclass::text cast).  Indented query looks like this:</p>
<pre style="padding-left: 30px;">select src_table, dst_table, fk_name, pg_size_pretty(s_size) as s_size, pg_size_pretty(d_size) as d_size, d
    from (
        select
                distinct on (1,2,3,4,5)
                textin(regclassout(c.conrelid))  as src_table,
                textin(regclassout(c.confrelid)) as dst_table,
                c.conname                        as fk_name,
                pg_relation_size(c.conrelid)     as s_size,
                pg_relation_size(c.confrelid)    as d_size,
                array_upper(di.indkey::int[], 1) + 1 - array_upper(c.conkey::int[], 1) as d
        from pg_constraint c
        left join pg_index di on di.indrelid = c.conrelid
                and array_to_string(di.indkey, ' ') ~ ('^' || array_to_string(c.conkey, ' ') || '( |$)')
        join pg_stat_user_tables st on st.relid = c.conrelid
        where c.contype = 'f'
        order by 1,2,3,4,5,6 asc
    ) mfk
    where mfk.d is distinct from 0 and mfk.s_size &gt; 1000000
    order by mfk.s_size desc, mfk.d desc;</pre>
<p>Note: don&#8217;t blindly add indexes basing on the results of this query.</p>
]]></content:encoded>
			<wfw:commentRss>http://radek.cc/2009/09/05/psqlrc-tricks-indexes/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>PostgreSQL tuning: temp_buffers</title>
		<link>http://radek.cc/2009/09/02/postgresql-tuning-temp_buffers/</link>
		<comments>http://radek.cc/2009/09/02/postgresql-tuning-temp_buffers/#comments</comments>
		<pubDate>Wed, 02 Sep 2009 12:22:38 +0000</pubDate>
		<dc:creator>radek</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[gotcha]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[postgresql]]></category>

		<guid isPermaLink="false">http://radek.cc/?p=146</guid>
		<description><![CDATA[A client of mine wanted to significantly increase the number of users connecting to his database.  All of these are long-running sessions (the whole working day), running complex queries (lots of joins, also to set returning functions (ab)using temporary tables, business logic kept mostly on the DB side).
A new beefy machine has been bought, postgresql.conf [...]]]></description>
			<content:encoded><![CDATA[<p>A client of mine wanted to significantly increase the number of users connecting to his database.  All of these are long-running sessions (the whole working day), running complex queries (lots of joins, also to <a href="http://www.postgresql.org/docs/current/static/functions-srf.html">set returning</a> functions (ab)using temporary tables, business logic kept mostly on the DB side).</p>
<p>A new beefy machine has been bought, postgresql.conf got reviewed independently by several seasoned DBAs, some performance tests were run&#8230; and we figured it&#8217;s going to be OK.</p>
<p>On the big day, the database started swapping and grinded to a halt.  After a long night of adding hacks to the application and more RAM to the machine, we got it to +- working state, but the performance was far from acceptable.</p>
<p>Then, Szymon the Project Manager has asked the Right Question™: why are the idle backends not freeing the memory, actually?  This got me back to studying the config file and I noticed &#8220;<a href="http://www.postgresql.org/docs/current/static/runtime-config-resource.html#GUC-TEMP-BUFFERS">temp_buffers</a> = 8GB&#8221;.  We all missed it before &#8212; the default is also 8, but MB, not GB.  Also, I thought the allocated memory is being freed after the temporary table is dropped&#8230;</p>
<p>Wrong.  The temp_buffers memory is not being freed.  It&#8217;s being kept <span style="text-decoration: underline;">until the end of the session</span>.</p>
<p>So, when the users log in, they usually run a heavy &#8220;what&#8217;s going on today&#8221; query, which includes a function creating a large temporary table.  Each backend has been allocating ~1GB of RAM for it and keeping it through the day.</p>
<p>Epic fail, but with a happy ending &#8212; everything is humming nicely now. :-)</p>
]]></content:encoded>
			<wfw:commentRss>http://radek.cc/2009/09/02/postgresql-tuning-temp_buffers/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>~/.psqlrc tricks: table sizes</title>
		<link>http://radek.cc/2009/08/15/psqlrc-tricks-table-sizes/</link>
		<comments>http://radek.cc/2009/08/15/psqlrc-tricks-table-sizes/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 11:08:35 +0000</pubDate>
		<dc:creator>radek</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[macros]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[psql]]></category>

		<guid isPermaLink="false">http://radek.cc/?p=131</guid>
		<description><![CDATA[~/.psqlrc macros for listing table sizes in the current database.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.postgresql.org/docs/current/static/app-psql.html">psql</a> from PostgreSQL 8.4.0 contains an useful addition: <code>\d+</code> shows table sizes.  That&#8217;s handy, but:</p>
<ul>
<li>doesn&#8217;t help much with 8.3 and 8.2 databases,</li>
<li>doesn&#8217;t let me run aggregates on these,</li>
<li>the view is cluttered by table comments,</li>
<li>can&#8217;t be sorted by size.</li>
</ul>
<p>So, my <code>~/.psqlrc</code> contains these two lines:</p>
<pre style="padding-left: 30px; white-space: pre-wrap">$ tail -n2 .psqlrc
<span style="text-decoration: underline;">\set rtsize</span> '(select table_schema, table_name, pg_relation_size( quote_ident( table_schema ) || \'.\' || quote_ident( table_name ) ) as size, pg_total_relation_size( quote_ident( table_schema ) || \'.\' || quote_ident( table_name ) ) as total_size  from information_schema.tables where table_type = \'BASE TABLE\' and table_schema not in (\'information_schema\', \'pg_catalog\') order by pg_relation_size( quote_ident( table_schema ) || \'.\' || quote_ident( table_name ) ) desc, table_schema, table_name)'
<span style="text-decoration: underline;">\set tsize</span> '(select table_schema, table_name, pg_size_pretty(size) as size, pg_size_pretty(total_size) as total_size from (:rtsize) x order by x.size desc, x.total_size desc, table_schema, table_name)'</pre>
<p>Both queries return the same data; &#8220;<code>:rtsize</code>&#8221; outputs raw numbers (mnemonic: raw table size), and &#8220;<code>:tsize</code>&#8221; formats them with <a href="http://www.postgresql.org/docs/current/static/functions-admin.html#FUNCTIONS-ADMIN-DBSIZE">pg_size_pretty()</a>.  Usage and the benefits will be best illustrated by examples; this one shows the 5 largest tables:</p>
<pre style="padding-left: 30px;"><a href="http://www.redmine.org/">redmine</a>=# :tsize limit 5;
 table_schema |      table_name       |  size  | total_size
--------------+-----------------------+--------+------------
 public       | journals              | 192 kB | 360 kB
 public       | issues                | 128 kB | 248 kB
 public       | wiki_content_versions | 48 kB  | 304 kB
 public       | journal_details       | 48 kB  | 152 kB
 public       | attachments           | 32 kB  | 88 kB
(5 rows)</pre>
<p>Sum of table sizes from the &#8220;log&#8221; schema, without pricing-related tables; note the &#8220;<code>x</code>&#8221; after &#8220;<code>:rtsize</code>&#8221; &#8212; since the macro is expanded to &#8220;<code>( select ... )</code>&#8220;, it has to be aliased:</p>
<pre style="padding-left: 30px;">msawi11=# select pg_size_pretty( sum(size)::int ), pg_size_pretty( sum(total_size)::int )
          from :rtsize x where table_schema='log' and table_name !~ 'ceny';
 pg_size_pretty | pg_size_pretty
----------------+----------------
 18 MB          | 32 MB
(1 row)</pre>
<p>Useful.</p>
<p>It&#8217;s a pity PostgreSQL doesn&#8217;t come with such <a href="http://pgfoundry.org/projects/newsysviews/">views</a> in the standard installation; <code>psql</code>&#8217;s code would have been much simpler, some incompatibilities of <code>\X</code> commands could have been avoided, and other client application developers wouldn&#8217;t have to copy/paste code from &#8220;<code>psql -E</code>&#8221; output&#8230;</p>
<p>Update: more <a href="http://radek.cc/tag/psql+macros/">~/.psqlrc tricks</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://radek.cc/2009/08/15/psqlrc-tricks-table-sizes/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>PostgreSQL: selecting from views with DISTINCT ON (&#8230;)</title>
		<link>http://radek.cc/2009/08/03/pg-views-with-distinct-on/</link>
		<comments>http://radek.cc/2009/08/03/pg-views-with-distinct-on/#comments</comments>
		<pubDate>Mon, 03 Aug 2009 20:37:20 +0000</pubDate>
		<dc:creator>radek</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[gotcha]]></category>
		<category><![CDATA[performance]]></category>
		<category><![CDATA[postgresql]]></category>

		<guid isPermaLink="false">http://radek.cc/?p=116</guid>
		<description><![CDATA[Solution for / description of a tricky PostgreSQL planner behaviour, involving DISTINCT ON and views.]]></description>
			<content:encoded><![CDATA[<p>Took me a half day to figure this out on a complex query, so I took the time to prepare a simple case for a demonstration.</p>
<p>First, see the documentation on <a href="http://www.postgresql.org/docs/current/static/sql-select.html#SQL-DISTINCT"><code>DISTINCT ON</code></a>; it&#8217;s a <a href="http://postgresql.org/">PostgreSQL</a>-only extension.</p>
<p>Consider a table structure: a <em>collection</em> has many <em>albums</em>, an album has many <em>tracks</em>.  Both an album and a track can have an optional <em>theme</em> attribute.  The task: find the list of tracks from a (single) given collection, and for each track, suggest one other album with the same theme from the same collection.</p>
<p>A full example with create table / create index statements is here: <a href="http://radek.cc/wp-content/uploads/2009/08/pg-view-distinct-on.sql">pg-view-distinct-on.sql</a>.  This example could be solved with a <code>GROUP BY</code> instead of a <code>DISTINCT ON ()</code>, but in my real query it wouldn&#8217;t work so great.</p>
<p>Let&#8217;s start with a simple view, giving us all the tracks from all collections:</p>
<pre style="padding-left: 30px;">create or replace view v_test as
    select a.collection_id, c.id as c_id, t.*
    from tracks      t
    join albums      a on a.id = t.album_id
    join collections c on c.id = a.collection_id
    left join albums r on
            r.id &lt;&gt; a.id and r.id &lt;&gt; t.album_id  -- we don't want the same album
        and r.collection_id = a.collection_id
    --  and r.collection_id = c.id               -- or would this be better...?
        and r.theme = t.theme;</pre>
<p>This is very nice, but duplicates the &#8220;track&#8221; rows, if more than one album with the same theme is found.  My approach looked like this:</p>
<pre style="padding-left: 30px;">create or replace view v_test_bad as
    select distinct on (id) *
    from v_test
    order by id;</pre>
<p>Now, &#8220;<code>select * from v_test_bad where collection_id = 42</code>&#8221; gives the correct results.  The problem is, it takes way too long &#8212; see the <a href="http://explain.depesz.com/s/s8">query plan</a>.  Indexes are not used at all.</p>
<p>Why, wtf?</p>
<p>The magic answer is: since we&#8217;re sorting the result (order by), then picking the <em>first row</em> from the <em>group</em> defined by the list of columns we&#8217;re <em>sorting with</em> (order by id, distinct on (id)), the planner doesn&#8217;t push the <code>WHERE</code> condition downwards, to the <code>v_test</code> view.  It doesn&#8217;t figure out, that pushing (using) it there won&#8217;t make any difference to the results.</p>
<p>Since there is no usable search condition in the <code>v_test</code> view, it has to do sequential scans, and then filter out the results&#8230; on bigger tables and longer sorting conditions that would take hours to weeks.</p>
<p>Solution:</p>
<pre style="padding-left: 30px;">create or replace view v_test_good as
    select distinct on (id, <strong>c_id, collection_id</strong>) *
    from v_test
    where collection_id = c_id
    order by id, <strong>c_id, collection_id</strong>;</pre>
<p>Adding columns to the &#8220;order by&#8221; and &#8220;distinct on&#8221; allows it to push the conditions on these columns &#8220;downwards&#8221;.  The &#8220;<code>collection_id = c_id</code>&#8221; part is a hint, which lets us avoid specifying both columns in the outer <code>WHERE</code> to get index scans on both [edit: now I'm not sure where I saw benefit in that].  The query time is now 4 miliseconds instead of 4 seconds; compare <a href="http://explain.depesz.com/s/UGE">the new plan</a> for &#8220;<code>select * from v_test_good where collection_id = 12</code>&#8220;.</p>
<p>Obvious, when you think about it.  Not so obvious with a complex query joining a dozen tables, though.</p>
]]></content:encoded>
			<wfw:commentRss>http://radek.cc/2009/08/03/pg-views-with-distinct-on/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Building SQL statements from PL/pgSQL</title>
		<link>http://radek.cc/2009/07/28/building-sql-statements-from-plpgsql/</link>
		<comments>http://radek.cc/2009/07/28/building-sql-statements-from-plpgsql/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 09:22:36 +0000</pubDate>
		<dc:creator>radek</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[plpgsql]]></category>
		<category><![CDATA[postgresql]]></category>

		<guid isPermaLink="false">http://radek.cc/?p=86</guid>
		<description><![CDATA[PL/pgSQL functions for building INSERT, UPDATE and DELETE statements with hstore.]]></description>
			<content:encoded><![CDATA[<p>I need to build INSERT and UPDATE statements, then send them over <a href="http://www.postgresql.org/docs/current/static/dblink.html">dblink</a> to another database.  Seems like a fairly common task, but I couldn&#8217;t find anything ready-made anywhere&#8230; Below is a version using <span style="text-decoration: line-through;"><a href="http://www.postgresql.org/docs/current/static/hstore.html">hstore</a></span> hstore-new (you&#8217;ll <span style="text-decoration: line-through;">probably</span> need <a href="http://pgfoundry.org/projects/hstore-new/">hstore-new</a> with its neat new operators and the ability to cast RECORD variables to hstore to do anything useful this way).</p>
<p>Example calls:</p>
<pre style="padding-left: 30px;">radek=# select build_insert( 'foo', 'a=&gt;1, b=&gt;2009-01-02, c=&gt;"foo bar baz", d=&gt;NULL'::hstore );
                                    build_insert
-------------------------------------------------------------------------------------
 INSERT INTO foo ( a, b, c, d ) VALUES ( '1', '2009-01-02', 'foo bar baz', DEFAULT )
(1 row)

radek=# select build_update( 'foo', 'a=&gt;1, b=&gt;2009-01-02, c=&gt;null'::hstore, '{a,b}'::text[] );
                     build_update
------------------------------------------------------
 UPDATE foo SET c=NULL WHERE a='1' AND b='2009-01-02'
(1 row)

radek=# select build_delete( 'foo', 'a=&gt;1, b=&gt;2009-01-02, c=&gt;null'::hstore, '{c}'::text[] );
          build_delete
---------------------------------
 DELETE FROM foo WHERE c IS NULL
(1 row)</pre>
<p>You should quote the table / schema name (the first argument) with <a href="http://www.postgresql.org/docs/current/static/functions-string.html">quote_ident()</a>, like this:</p>
<pre style="padding-left: 30px;">quote_ident( 'public' ) || '.' || quote_ident( 'foo' )</pre>
<p>Tests and comments most welcome; here&#8217;s the code:</p>
<pre style="padding-left: 30px;">create or replace function build_insert( _table text, _record hstore ) returns text as $$
declare
	_sql	text;
	_values	text;
	_tmp	record;
begin
	for _tmp in ( select * from each( _record ) order by key ) loop
		_sql := coalesce( _sql || ', ', 'INSERT INTO ' || _table || ' ( ' )
			|| quote_ident( _tmp.key );
		_values := coalesce( _values || ', ', 'VALUES ( ' )
			|| coalesce( quote_literal( _tmp.value ), 'DEFAULT' );
	end loop;
	return _sql || ' ) ' || _values || ' )';
end;
$$ language plpgsql immutable strict;

comment on function build_insert( text, hstore ) is
  'build an INSERT statement from the hstore, the table name should be quoted';

create or replace function build_update( _table text, _record hstore, _pk text[] ) returns text as $$
declare
	_sql	text;
	_where	text;
	_tmp	record;
begin
	for _tmp in ( select * from each( _record - _pk ) order by key ) loop
		_sql := coalesce( _sql || ', ', 'UPDATE ' || _table || ' SET ' )
			|| quote_ident( _tmp.key ) || '='
			|| coalesce( quote_literal( _tmp.value ), 'NULL' );
	end loop;
	for _tmp in ( select * from each( _record =&gt; _pk ) order by key ) loop
		_where := coalesce( _where || ' AND ', '' )
			|| quote_ident( _tmp.key )
			|| coalesce( '=' || quote_literal( _tmp.value ), ' IS NULL' );
	end loop;
	return _sql || ' WHERE ' || _where;
end;
$$ language plpgsql immutable strict;

comment on function build_update( text, hstore, text[] ) is
  'build an UPDATE statement, pass the list of primary key columns in the last argument';

create or replace function build_delete( _table text, _record hstore, _pk text[] ) returns text as $$
declare
	_where	text;
	_tmp	record;
begin
	for _tmp in ( select * from each( _record =&gt; _pk ) order by key ) loop
		_where := coalesce( _where || ' AND ', '' )
			|| quote_ident( _tmp.key )
			|| coalesce( '=' || quote_literal( _tmp.value ), ' IS NULL' );
	end loop;
	return 'DELETE FROM ' || _table || ' WHERE ' || _where;
end;
$$ language plpgsql immutable strict;

comment on function build_delete( text, hstore, text[] ) is
  'builds a DELETE statement with WHERE columns specified as the last argument';</pre>
<p>Edit: noted the need for quote_ident() and hstore-new.</p>
]]></content:encoded>
			<wfw:commentRss>http://radek.cc/2009/07/28/building-sql-statements-from-plpgsql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PostgreSQL designer&#8217;s toolbox</title>
		<link>http://radek.cc/2008/12/07/postgresql-designers-toolbox/</link>
		<comments>http://radek.cc/2008/12/07/postgresql-designers-toolbox/#comments</comments>
		<pubDate>Sun, 07 Dec 2008 16:49:31 +0000</pubDate>
		<dc:creator>radek</dc:creator>
				<category><![CDATA[Technical]]></category>
		<category><![CDATA[dia]]></category>
		<category><![CDATA[diagrams]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[postgresql]]></category>
		<category><![CDATA[uml]]></category>

		<guid isPermaLink="false">http://radek.cc/?p=53</guid>
		<description><![CDATA[Note: this article is a bit outdated, tedia2sql doesn&#8217;t seem to be maintained anymore.  Take a look at Parse::Dia::SQL (the output SQL is different, though).  I&#8217;ll update this article after I&#8217;ll finally switch my scripts&#8230; but that is not likely to happen soon.
Tools I&#8217;m using while developing PostgreSQL databases:

Dia
tedia2sql
make

First, I&#8217;m drawing the database schema in [...]]]></description>
			<content:encoded><![CDATA[<p style="padding-left: 30px;"><em><strong>Note</strong></em>: this article is a bit outdated, tedia2sql doesn&#8217;t seem to be maintained anymore.  Take a look at <a href="http://search.cpan.org/dist/Parse-Dia-SQL/">Parse::Dia::SQL</a> (the output SQL is different, though).  I&#8217;ll update this article after I&#8217;ll finally switch my scripts&#8230; but that is <a href="http://radek.cc/category/travel/">not likely to happen soon</a>.</p>
<p>Tools I&#8217;m using while developing <a href="http://www.postgresql.org/">PostgreSQL</a> databases:</p>
<ol>
<li><a href="http://live.gnome.org/Dia/">Dia</a></li>
<li><a href="http://tedia2sql.tigris.org/">tedia2sql</a></li>
<li>make</li>
</ol>
<p>First, I&#8217;m drawing the database schema in <code>Dia</code>, then converting it into a <code>*.sql</code> file with <code>CREATE TABLE</code> statements using <code>tedia2sql</code>.  When a full <code>DROP</code> / <code>CREATE</code> cycle is not feasible (eg. the database is in production already), I&#8217;m preparing the <code>ALTER TABLE</code> statements manually, but a change to the diagram <em>always goes first</em>.</p>
<p>Other people on the team appreciate the ability to create the database easily and without touching the production instance, and having an up-to-date, easily accessible PNG file with the diagram:</p>
<div id="attachment_69" class="wp-caption aligncenter" style="width: 510px"><a href="http://radek.cc/wp-content/uploads/2009/01/pgdia.png"><img class="size-full wp-image-69" title="Example Dia output" src="http://radek.cc/wp-content/uploads/2009/01/pgdia.png" alt="" width="500" height="552" /></a><p class="wp-caption-text">Example Dia output</p></div>
<p>It&#8217;s useful to wrap the <code>*.sql</code> file, generated by <code>tedia2sql</code> in a hand-crafted <code>*.sql</code> with <code>CREATE DATABASE</code> statements and other (eg. <code>ALTER TABLE ADD CONSTRAINT</code>) parts which <span style="text-decoration: line-through;"><code>tedia2sql</code> can&#8217;t generate</span> <span style="color: #333300;">you might not want to generate using <code>tedia2sql</code></span>; see <a href="http://github.com/radekz/pgdia/tree/master/create.sql"><code>create.sql</code></a> for an example.  It&#8217;s also handy when your database should always be populated with some data (eg. dictionary tables), uses pl/pgsql functions, triggers, etc.</p>
<p>A full example / template to use in your project: <a href="http://radek.cc/pgdia-1.01.tar.gz"><code>pgdia-1.01.tar.gz</code></a> (public domain); customize it by replacing all mentions of &#8220;<code>pgdia</code>&#8221; with your project&#8217;s name:</p>
<pre style="padding-left: 30px;">perl -pi -e 's/pgdia/projectname/g' create.sql Makefile</pre>
<p>A handy tool (if you&#8217;re not starting from scratch) is <a href="http://www.rbt.ca/autodoc/">PostgreSQL Autodoc</a>.  It allows you to generate a <code>Dia</code> file from an existing database schema.  It does not generate <code>tedia2sql</code>-compatible diagrams at the moment, but it seems to be just a matter of editing a template file.</p>
<p>Another useful tool for analysing an existing database: <a href="http://schemaspy.sourceforge.net/">SchemaSpy</a></p>
<p>Thanks to <a href="http://www.depesz.com/">depesz</a> for showing me <code>Dia</code> and <code>tedia2sql</code>, years ago&#8230; :-)</p>
<p>If you know of a more productive (or just better) tools / workflow (usable on Linux), please let me know.</p>
<p>Edit 2009-01-09: updated the diagram to include a generated INSERT and arbitrary DDL.</p>
<p>Edit 2009-06-06: SchemaSpy.</p>
]]></content:encoded>
			<wfw:commentRss>http://radek.cc/2008/12/07/postgresql-designers-toolbox/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

