One process per query, one thread per process. So you can't use more than one CPU core for a query. This is improving in 9.6 and will continue to improve, but for now it means Pg doesn't work as well for workloads with fewer big CPU heavy queries.
Limited disk parallelism can fail to saturate modern storage subsystems
No parallelism on standby recovery means the standby may not be able to keep up with the master's load on identical hardware.
No built-in logical replication. Built-in block based replication replicates every table in every database without any filtering options. All or nothing.
The MONEY
type is horrible. Run away. Use NUMERIC
.
NUMERIC
is binary-coded decimal strings, not a 64 or 128 bit fixed decimal floating point type. Since only a few platforms like IBM POWER 7 have hardware acceleration support this doesn't turn out to matter very much.
If different clients have different encodings set, and/or different databases have different default encodings, the PostgreSQL log files will contain a mangled mix of different text encodings and languages. There is no reliable way to tell which encoding a given line is in. Don't mix encodings, just use UTF-8.
Don't use more than 1000-ish columns. There's a hard limit somewhere around 1400, depending on data type, but usually lower.
Table inheritance doesn't play well with FK constraints. You can't have FK constraints that reference all child tables, because a unique index cannot span the parent table and its children. In general you choose inheritance or FK constraints. Since inheritance is your main option for partitioning, this can suck.
There's no lazy return of big bytea
(blob) columns, etc. You have to leave them out of the initial resultset then fetch them by a second query if you want them, or just receive them at the start.
No true stored procedures. You can't commit an autonomous transaction within a procedure, return multiple result sets, etc. You have to work around autonomous tx's with a second connection made from within the procedure (slow!) or live without. Multiple result sets can be emulated by named cursors to some degree.
No table-valued variables. You can't easily pass a set or table into a function. You can use named cursors to work around this though.