Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a PostgreSQL partitioned sequence?

Is there a simple (ie. non-hacky) and race-condition free way to create a partitioned sequence in PostgreSQL. Example:

Using a normal sequence in Issue:

| Project_ID | Issue |
| 1          | 1     |
| 1          | 2     |
| 2          | 3     |
| 2          | 4     |

Using a partitioned sequence in Issue:

| Project_ID | Issue |
| 1          | 1     |
| 1          | 2     |
| 2          | 1     |
| 2          | 2     |
like image 774
FooBar Avatar asked Aug 28 '10 15:08

FooBar


People also ask

How do I partition an existing table in PostgreSQL?

You can use the ALTER TABLE… ADD PARTITION statement to add a partition to a table with a DEFAULT rule as long as there are no conflicting values between existing rows in the table and the values of the partition to be added.

How do I split a partition in PostgreSQL?

Use the ALTER TABLE… SPLIT PARTITION command to divide a single partition into two partitions, maintaining the partitioning of the original table in the newly created partitions, and redistributing the partition's contents between the new partitions. The command syntax comes in two forms.

Does Postgres create partition automatically?

Table partitioning is one of the best-liked features out of the more recent PostgreSQL developments. However, there is no support for automatic partition creation yet.


1 Answers

I do not believe there is a simple way that is as easy as regular sequences, because:

  1. A sequence stores only one number stream (next value, etc.). You want one for each partition.
  2. Sequences have special handling that bypasses the current transaction (to avoid the race condition). It is hard to replicate this at the SQL or PL/pgSQL level without using tricks like dblink.
  3. The DEFAULT column property can use a simple expression or a function call like nextval('myseq'); but it cannot refer to other columns to inform the function which stream the value should come from.

You can make something that works, but you probably won't think it simple. Addressing the above problems in turn:

  1. Use a table to store the next value for all partitions, with a schema like multiseq (partition_id, next_val).
  2. Write a multinextval(seq_table, partition_id) function that does something like the following:

    1. Create a new transaction independent on the current transaction (one way of doing this is through dblink; I believe some other server languages can do it more easily).
    2. Lock the table mentioned in seq_table.
    3. Update the row where the partition id is partition_id, with an incremented value. (Or insert a new row with value 2 if there is no existing one.)
    4. Commit that transaction and return the previous stored id (or 1).
  3. Create an insert trigger on your projects table that uses a call to multinextval('projects_table', NEW.Project_ID) for insertions.

I have not used this entire plan myself, but I have tried something similar to each step individually. Examples of the multinextval function and the trigger can be provided if you want to attempt this...

like image 104
Edmund Avatar answered Sep 20 '22 07:09

Edmund