A new set of APIs for foreign data wrappers has been added to allow writable operations on foreign sources. This feature has been committed by Tom Lane a couple of days ago.
commit 21734d2fb896e0ecdddd3251caa72a3576e2d415
Author: Tom Lane
Date: Sun Mar 10 14:14:53 2013 -0400
 
Support writable foreign tables.
 
This patch adds the core-system infrastructure needed to support updates
on foreign tables, and extends contrib/postgres_fdw to allow updates
against remote Postgres servers. There's still a great deal of room for
improvement in optimization of remote updates, but at least there's basic
functionality there now.
 
KaiGai Kohei, reviewed by Alexander Korotkov and Laurenz Albe, and rather
heavily revised by Tom Lane.

Based on the documentation, the implementation is still very basic as nothing is done with clause shippability. Just to give some notions about that: roughly a clause in a SELECT query (LIMIT, OFFSET, GROUP BY, HAVING, ORDER BY, etc.) is shippable if this clause can be entirely evaluated on remote server, making less processing happening on local server, and reducing the tuple selectivity. A direct consequence of clause shippability limitation is that UPDATE and DELETE queries can take quite a long time if they are run on many rows because query is run in two steps:

  • Scan remote table and fetch back to local server the tuples to be manipulated
  • Process UPDATE or DELETE based on the tuples fetched

INSERT does not need such scan as in this case new data is simply sent to the remote table, the tuple values being computed before sending the query (even for immutable functions). Not really performant but it is the safest approach. Postgres-XC has similar and more advanced features for foreign DDL planning and execution in its core (some of them implemented by me), have a look for example at this article I wrote a while ago.

It is possible to test writable foreign tables with postgres_fdw as it has been extended to support this new feature. So let’s give it a try with two postgres servers using ports 5432 and 5433. Server with port 5432 has postgres_fdw installed and will interact with the remote server running under port 5433. In order to get the basics of postgres_fdw, you can refer to this article written a couple of weeks ago.

Now, it is time to test the feature. First let’s create a table on remote server.
$ psql -p 5433 -c "CREATE TABLE aa_remote (a int, b int)" postgres
CREATE TABLE

Then it is necessary to create a foreign table on the local server.
postgres=# CREATE SERVER postgres_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'localhost', port '5433', dbname 'postgres');
CREATE SERVER
postgres=# CREATE USER MAPPING FOR PUBLIC SERVER postgres_server OPTIONS (password '');
CREATE USER MAPPING
postgres=# CREATE FOREIGN TABLE aa_foreign (a int, b int) SERVER postgres_server OPTIONS (table_name 'aa_remote');
CREATE FOREIGN TABLE

Then let’s test the new feature by performing some DML operations on the foreign table from local server.
postgres=# INSERT into aa_foreign values (1,2);
INSERT 0 1
postgres=# select * from aa_foreign;
a | b
---+---
1 | 2
(1 row)
postgres=# update aa_foreign set b = 3;
UPDATE 1
postgres=# select * from aa_foreign;
a | b
---+---
1 | 3
(1 row)

Everything is going well on local side, and on remote side what happened?
$ psql -p 5433 -c 'SELECT * FROM aa_remote' --dbname postgres
a | b
---+---
1 | 3
(1 row)

So the data of the remote table has been correctly changed from local server.

Just before the tests, I explained that a scan is done for UPDATE and DELETE before actually running the DML, you can get more details about that with EXPLAIN.
postgres=# explain verbose update aa_foreign set b = 3;
QUERY PLAN
-----------------------------------------------------------------------------------
Update on public.aa_foreign (cost=100.00..182.27 rows=2409 width=10)
 Remote SQL: UPDATE public.aa_remote SET b = $2 WHERE ctid = $1
 -> Foreign Scan on public.aa_foreign (cost=100.00..182.27 rows=2409 width=10)
  Output: a, 3, ctid
  Remote SQL: SELECT a, NULL, ctid FROM public.aa_remote FOR UPDATE
(5 rows)

In the case of postgres_fdw, selectivity of tuple is done with ctid of tuple, which ensures tuple uniqueness. Note that if you implement your own foreign data wrapper, you might need to use columns having primary keys for selectivity of tuples.

There are also a couple of things to be aware of when using this feature.

  • There are risk of data incompatibility for data formatted with GUC parameters. This has been mentionned in the community but try for example to manipulate servers with different settings of datesyle…
  • Transactions are open on remote server using repeatable read.
  • UPDATE and DELETE can be costly if scan is done with a good-old-fashioned sequential scan, but well that’s a known thing
  • Things I forgot…

Setting up logging for a PostgreSQL server using syslog on a Linux machine is intuitive especially with logging systems like syslog-ng, you just need to put the correct parameters at the right place.

First, you need to setup the system side, by adding the following settings in /etc/syslog-nd/syslog-nd.conf (or similar, don’t hesitate to customize that with your own paths).
destination postgres { file("/var/log/pgsql"); };
filter f_postgres { facility(local0); };
log { source(src); filter(f_postgres); destination(postgres); };

This will send all the logs of postgresql server to /var/log/pgsql. Be sure to combine that with some solution rotating log files to avoid a single file becoming too large… And reload syslog-ng with a command similar to that (varies depending on distribution used, here Archlinux).
systemctl reload syslog-ng

Then, you need to add those settings in postgresql.conf.
log_destination = 'syslog' # Can specify multiple destinations
syslog_facility='LOCAL0'
syslog_ident='postgres'

Based on the documentation, syslog_facility can be set from LOCAL0 to LOCAL7.
Don’t forget that you can also specify multiple log destinations. For example when using stderr and syslog at the same time, simply do that:
log_destination = 'stderr,syslog'

Finally, reload the parameters of server and you are done.
pg_ctl reload -D $PGDATA
Note that restarting the server is not necessary.

PostgreSQL 9.3 comes with a pretty cool feature called materialized views. It has been created by Kevin Grittner and committed by the same person not so long ago.
commit 3bf3ab8c563699138be02f9dc305b7b77a724307
Author: Kevin Grittner
Date: Sun Mar 3 18:23:31 2013 -0600
 
Add a materialized view relations.
 
A materialized view has a rule just like a view and a heap and
other physical properties like a table. The rule is only used to
populate the table, references in queries refer to the
materialized data.
 
This is a minimal implementation, but should still be useful in
many cases. Currently data is only populated "on demand" by the
CREATE MATERIALIZED VIEW and REFRESH MATERIALIZED VIEW statements.
It is expected that future releases will add incremental updates
with various timings, and that a more refined concept of defining
what is "fresh" data will be developed. At some point it may even
be possible to have queries use a materialized in place of
references to underlying tables, but that requires the other
above-mentioned features to be working first.
 
Much of the documentation work by Robert Haas.
Review by Noah Misch, Thom Brown, Robert Haas, Marko Tiikkaja
Security review by KaiGai Kohei, with a decision on how best to
implement sepgsql still pending.

What is a materialized view? In short, it is the mutant of a table and a view. A view is a projection of data in a given relation and has no storage. A table is well… A table…
Between that, a materialized view is a projection of table data and has its own storage. It uses a query to fetch its data like a view, but this data is stored like a common table. The materialized view can also be refreshed with updated data by running once again the query it uses for its projection, or have its data truncated. In the last case it is left in an non-scannable state. Also, as a materialized view has its proper storage, it can use tablespaces and its own indexes. Note that it can also be an unlogged relation.

This feature introduces four new SQL commands:

CREATE, ALTER and DROP are common DDL commands here to manipulate the definition of materialized views. What is important here is the new command REFRESH (its name has been a long debate inside the community). This command can be used to update the materialized view with fresh data by running once again the scanning query. Note that REFRESH can also be used to *truncate* (not really though) the data of the relation by running it with the clause WITH NO DATA.

Materialized views have their own advantages in many scenarios: faster access to data than needs to be brought from a remote server (read a file on postgres server through file_fdw, etc.), using data that needs to be refreshed periodically (cache system), projecting data with embedded ORDER BY from a large table, running an expensive join in background periodically, etc.

I can also imagine some nice combinations with data refresh and custom background workers. Who said that automatic data refresh on a materialized view was not possible?

Now let’s have a look at how it works.
postgres=# CREATE TABLE aa AS SELECT generate_series(1,1000000) AS a;
SELECT 1000000
postgres=# CREATE VIEW aav AS SELECT * FROM aa WHERE a <= 500000;
CREATE VIEW
postgres=# CREATE MATERIALIZED VIEW aam AS SELECT * FROM aa WHERE a <= 500000;
SELECT 500000

Here is the size that each relation uses.
postgres=# SELECT pg_relation_size('aa') AS tab_size, pg_relation_size('aav') AS view_size, pg_relation_size('aam') AS matview_size;
tab_size | view_size | matview_size
----------+-----------+--------------
36249600 | 0 | 18137088
(1 row)

A materialized view uses storage (here 18M), as much as it needs to store the data it fetched from its parent table (with size of 36M) when running the view query.

The refresh of a materialized view can be controlled really easily.
postgres=# DELETE FROM aa WHERE a <= 500000;
DELETE 500000
postgres=# SELECT count(*) FROM aam;
count
--------
500000
(1 row)
postgres=# REFRESH MATERIALIZED VIEW aam;
REFRESH MATERIALIZED VIEW
postgres=# SELECT count(*) FROM aam;
count
-------
0
(1 row)

The new status of table aa is effective on its materialized view aam only once REFRESH has been kicked. Note that at the time of this post, REFRESH uses an exclusive lock (ugh...).

A materialized view can also be set as not scannable thanks to the clause WITH NO DATA of REFRESH.
postgres=# REFRESH MATERIALIZED VIEW aam WITH NO DATA;
REFRESH MATERIALIZED VIEW
postgres=# SELECT count(*) FROM aam;
ERROR: materialized view "aam" has not been populated
HINT: Use the REFRESH MATERIALIZED VIEW command.

There is a new catalog table to help you find the current state of materialized views called pg_matviews.
postgres=# SELECT matviewname, isscannable FROM pg_matviews;
matviewname | isscannable
-------------+-------------
aam | f
(1 row)

It is also not possible to run DML queries on it. This makes sense as the data this view has might not reflect the current state of its parent relation(s). On the contrary, a simple view runs its underlying query each time it is needed, so a parent table could be modified through it (per se updatable views).
postgres=# INSERT INTO aam VALUES (1);
ERROR: cannot change materialized view "aam"
postgres=# UPDATE aam SET a = 5;
ERROR: cannot change materialized view "aam"
postgres=# DELETE FROM aam;
ERROR: cannot change materialized view "aam"

Now, a couple of words about performance improvement and degradation you can have with materialized views as you can manipulate indexes on those relations. For example, it is easily possible to improve queries on the materialized views without caring about the schema of its parent relations.
postgres=# EXPLAIN ANALYZE SELECT * FROM aam WHERE a = 1;
QUERY PLAN
--------------------------------------------------------------------------------------------------
Seq Scan on aam (cost=0.00..8464.00 rows=1 width=4) (actual time=0.060..155.934 rows=1 loops=1)
Filter: (a = 1)
Rows Removed by Filter: 499999
Total runtime: 156.047 ms
(4 rows)
postgres=# CREATE INDEX aam_ind ON aam (a);
CREATE INDEX
postgres=# EXPLAIN ANALYZE SELECT * FROM aam WHERE a = 1;
QUERY PLAN
------------------------------------------------------------------------------------------------------------------
Index Only Scan using aam_ind on aam (cost=0.42..8.44 rows=1 width=4) (actual time=2.096..2.101 rows=1 loops=1)
Index Cond: (a = 1)
Heap Fetches: 1
Total runtime: 2.196 ms
(4 rows)

Take care also that indexes and constraint (materialized views can have constraints!) of the parent relation are not copied with the materialized view. For example, a fast query scanning some table's primary key might finish with a deadly sequential scan if it is run on an underlying materialized view based on this table.
postgres=# INSERT INTO bb VALUES (generate_series(1,100000));
INSERT 0 100000
postgres=# EXPLAIN ANALYZE SELECT * FROM bb WHERE a = 1;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------
Index Only Scan using bb_pkey on bb (cost=0.29..8.31 rows=1 width=4) (actual time=0.078..0.080 rows=1 loops=1)
Index Cond: (a = 1)
Heap Fetches: 1
Total runtime: 0.159 ms
(4 rows)
postgres=# CREATE MATERIALIZED VIEW bbm AS SELECT * FROM bb;
SELECT 100000
postgres=# EXPLAIN ANALYZE SELECT * FROM bbm WHERE a = 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------
Seq Scan on bbm (cost=0.00..1776.00 rows=533 width=4) (actual time=0.144..41.873 rows=1 loops=1)
Filter: (a = 1)
Rows Removed by Filter: 99999
Total runtime: 41.935 ms
(4 rows)

Such designs are of course not recommended on a production system, only be aware that bad designs will badly impact your application performance (that's always the case btw).

It is really a nice thing to have particularly for caching applications! So enjoy!

pg_xlogdump is a new contrib module introduced in PostgreSQL 9.3 by this commit.
commit 639ed4e84b7493594860f56b78b25fd113e78fd7
Author: Alvaro Herrera
Date: Fri Feb 22 16:46:24 2013 -0300
 
Add pg_xlogdump contrib program
 
This program relies on rm_desc backend routines and the xlogreader
infrastructure to emit human-readable rendering of WAL records.
 
Author: Andres Freund, with many reworks by Alvaro
Reviewed (in a much earlier version) by Peter Eisentraut

Mainly useful for educational and debugging purposes, pg_xlogdump can be used to understand the internals of PostgreSQL by dumping the WAL (Write-ahead log, which is the basic mechanism used by the server for transaction replay during recovery) into a shape humanly readable.

Just a little bit more information about WAL… Its information is stored in files located in pg_xlog of $PGDATA whose name respect a format name subdivided into 3 sequences of 8 hexa digits defining:

  • Timeline ID
  • Block ID
  • Segment ID

The counter for blocks is incremented once segments are filled.

Postgres includes a couple of functions that can help you to determine in which file is located a given WAL record (pg_xlogfile_name) or what is the current WAL position (pg_current_xlog_location).
michael=# select pg_current_xlog_location();
pg_current_xlog_location
--------------------------
4A/1799988
(1 row)
michael=# select pg_xlogfile_name('4A/1799988');
pg_xlogfile_name
--------------------------
000000010000004A00000001
(1 row)

Here the server is currently on timeline 1, with a Block ID of 4A and a segment ID of 1. The composition of Block ID + segment ID is a LSN or log sequence number, for example ’4A/1799988′. The XLOG files are located in the folder pg_xlog of $PGDATA. Each file has a size of 16MB, and server switches to a new file once this maximum size is reached or once no new file has been written since a time of archive_timeout, parameter of postgresql.conf.

After this digression, let’s have a look at what this utility can do.
The only mandatory option is to specify a start from where the dump will be taken, by either specifying a start LSN with –start or a start WAL file with commands similar to that.
pg_xlogdump --start 0/010EA4D0
pg_xlogdump 000000010000000000000001

If no path is specified to scan the segment files in a given directory, the default is to look if there is a folder called pg_xlog in current directory and get results from it.
In a dump you will get information like that for each WAL record:
rmgr: Heap len (rec/tot): 143/ 175, tx: 688, lsn: 0/02010BE8, prev 0/02010BA0, bkp: 0000, desc: insert: rel 1663/16384/11782; tid 41/54
Each field being in more details:

  • rmgr, the resource manager involved, can be filtered with option -r/–rmgr
  • len, about the length of the record
  • tx, ID of transaction involved with this record, can be filtered with option -x/–xid
  • lsn, log sequence number, including the previous and current lsn, can be filtered with –start and –end.
  • bkp, for the backup block
  • desc, for the description of the action record is doing, and some information related to the relation and page item with which interacts the action

WAL records are divided by resource managers, like database, tablespace, sequence, heap, etc. Based on that you can filter the dump depending on resource manager you want to see. To get a complete list of the resource managers available, simply do that:
pg_xlogdump --rmgr=list

For example, let’s create a sequence on server.
postgres=# create sequence aas;
CREATE SEQUENCE

Here is what you dump when filtering for the resource manager Sequence.
$ pg_xlogdump --start 0/02000000 -r Sequence
rmgr: Sequence len (rec/tot): 158/ 190, tx: 688, lsn: 0/02013C18, prev 0/02013B60, bkp: 0000, desc: log: rel 1663/16384/16390

You can then for example dump all the WAL records for this transaction by running a command like that:
pg_xlogdump --start 0/02000000 -x 688

Only a short introduction of what you can do this module is provided in this post, so for fore details feel free to have a look at the documentation about the available options and what you can do (and cannot do) with pg_xlogdump. Honestly I think it is a good tool to understand the internals of Postgres for newcomers.

Up to Postgres 9.2, the only foreign data wrapper present in core was file_fdw, allowing you to query files as remote tables. This has been corrected with the addition of a second foreign data wrapper called postgres_fdw. This one simply allows to query foreign Postgres servers and fetch results directly on your local server. It has been introduced by this commit.
commit d0d75c402217421b691050857eb3d7af82d0c770
Author: Tom Lane
Date: Thu Feb 21 05:26:23 2013 -0500
 
Add postgres_fdw contrib module.
 
There's still a lot of room for improvement, but it basically works,
and we need this to be present before we can do anything much with the
writable-foreign-tables patch. So let's commit it and get on with testing.
 
Shigeru Hanada, reviewed by KaiGai Kohei and Tom Lane

Documentation can be found here for the time being.

In order to install it from source, do the following commands from the Postgres root folder.
cd contrib/postgres_fdw
make install

Then connect to your existing Postgres server and finish the installation with CREATE EXTENSION.
postgres=# CREATE EXTENSION postgres_fdw;
CREATE EXTENSION
postgres=# \dx postgres_fdw
List of installed extensions
Name | Version | Schema | Description
--------------+---------+--------+----------------------------------------------------
postgres_fdw | 1.0 | public | foreign-data wrapper for remote PostgreSQL servers
(1 row)

Now let’s test it with the case of a simple cluster with one slave running with port 5532 on the same server as its master. Here is the configuration.
$ psql -p 5532 -c 'select pg_is_in_recovery()'
pg_is_in_recovery
-------------------
t
(1 row)

When using a foreign data wrapper, you need to create first a server.
postgres=# CREATE SERVER postgres_server
postgres=# FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host 'localhost', port '5532', dbname 'postgres');
CREATE SERVER
postgres=# \des
List of foreign servers
Name | Owner | Foreign-data wrapper
-----------------+--------+----------------------
postgres_server | xxxxxx | postgres_fdw
(1 row)

Then let’s move on with a user mapping and a table to query.
postgres=# CREATE USER MAPPING FOR PUBLIC SERVER postgres_server OPTIONS (password '');
CREATE USER MAPPING
postgres=# CREATE TABLE aa AS SELECT 1 AS a, generate_series(1,3) AS b;
CREATE TABLE

As the foreign server used is the slave of our master, there is no need to create this table on the second node.

What remains is the creation of the foreign table.
postgres=# CREATE FOREIGN TABLE aa_foreign (a int, b int)
postgres=# SERVER postgres_server OPTIONS (table_name 'aa');
CREATE FOREIGN TABLE

Then if you query the foreign table.
postgres=# select * from aa_foreign;
a | b
---+---
1 | 1
1 | 2
1 | 3
(3 rows)

Yeah, done!

This feature still needs more testing, so go ahead and test it by yourself you might be surprised with the things you can do with it.

©2010-2013 Michael Paquier All content is ©Copyright of Otacoo.com 2010-2013. Privacy Policy - Terms of Use