Postgresql 9.1 enum type ordering doesn't work like I expect - postgresql

I've got an enum type that I've become interested in ordering in a particular way. I've written and run the SQL to impose a new ordering by sorting the labels (externally) by my new criteria and then updating the enumsortorder for all the values.
It doesn't work. I've verified that I've satisfied the (really weird) rule that the sort ordering feature works only on enum types with even oids; my oid for this type is even (58016). As far as I can tell, the ordering being imposed when I ORDER BY the enum column is exactly the same as what it was before.
Is there something else I need to do in order to make this work? The PostgreSQL documentation makes me think it should work.

Even oids have fixed ordering so you can't reorder them by modifying the pg-enum system table.
you're going to have to replace the existing enum with a new enum type. this means
Creating a new enum type
Dropping any relationships that use the enum.
Update the columns to the new type using something like
ALTER TABLE foo ALTER COLUMN bar
TYPE TO new_enum_type
USING (bar::text)::new_enum_type;
Here the cast to text matches the new enum values to the old enum values by their name.
Finally you need to recreate all the dropped relationships.
If needed you can run all this DML inside a transaction block.
Expect it to be slow if you have lots of data as it's rewriting whole
tables

Related

Weak points while comparing columns in trigger as hstore-data

On some occasion I'd like before UPDATE to make sure which columns are changed. To make it as generic as possible, I don't want to use schema, table or column names in function. I found some solution here in SO and other places, and particularly liked idea to use hstore from this answer
Downside of hstore, as said widely, is it that this way I lose data types, everything is stringified.
But using it in context of trigger (while having no complex cols like json or hstore), where both NEW and OLD have same set of cols with according datatypes, I could think of just one problem: NULL and empty values will be not distinguishable.
What other problems I may be faced with, when I detect changes in trigger function like this:
changes := hstore(NEW) - hstore(OLD);
Alternative seems to use jsonb and then write some jsonb_diff function to discover changes. hstore's offered internal subtract-operation seems way more robust, but maybe I have not considered all weak points there.
I'm using Postgres 9.6.

Partial inserts with Cassandra and Phantom DSL

I'm building a simple Scala Play app which stores data in a Cassandra DB using the Phantom DSL driver for Scala. One of the nice features of Cassandra is that you can do partial updates i.e. so long as you provide the key columns, you do not have to provide values for all the other columns in the table. Cassandra will merge the data into your existing record based on the key.
Unfortunately, it seems this doesn't work with Phantom DSL. I have a table with several columns, and I want to be able to do an update, specifying values just for the key and one of the data columns, and let Cassandra merge this into the record as usual, while leaving all the other data columns for that record unchanged.
But Phantom DSL overwrites existing columns with null if you don't specify values in your insert/update statement.
Does anybody know of a work-around for this? I don't want to have to read/write all the data columns every time, as eventually the data columns will be quite large.
FYI I'm using the same approach to my Phantom coding as in these examples:
https://github.com/thiagoandrade6/cassandra-phantom/blob/master/src/main/scala/com/cassandra/phantom/modeling/model/GenericSongsModel.scala
It would be great to see some code, but partial updates are possible with phantom. Phantom is an immutable builder, it will not override anything with null by default. If you don't specify a value it won't do anything about it.
database.table.update.where(_.id eqs id).update(_.bla setTo "newValue")
will produce a query where only the values you've explicitly set to something will be set to null. Please provide some code examples, your problem seems really strange as queries don't keep track of table columns to automatically add in what's missing.
Update
If you would like to delete column values, e.g set them to null inside Cassandra basically, phantom offers a different syntax which does the same thing:
database.table.delete(_.col1, _.col2).where(_.id eqs id)`
Furthermore, you can even delete map entries in the same fashion:
database.table.delete(_.props("test"), _.props("test2").where(_.id eqs id)
This assumes props is a MapColumn[Table, Record, String, _], as the props.apply(key: T) is typesafe, so it will respect the keytype you define for the map column.

what is the difference of type record and type row in PostgreSQL?

As title shown, when reading the manul, I found type record type and row type, which are both composite type. However, I want to figure out their difference.
They're similar once defined but tend to have different use cases.
A RECORD type has no predefined structure and is typically used when the row type might change or is out of your control, for example if you're referencing a record in a FOR LOOP.
ROWTYPE is predefined of a particular table row structure and thus if anything deviates from that structure you will get runtime errors.
It all depends what you're trying to achieve.
For cursor loops I use a RECORD>
For more information:
http://www.postgresql.org/docs/current/static/plpgsql-declarations.html

Rename enum item in PostgreSQL

I would like to change the name of an item in an enum type in PostgreSQL 9.1.5.
Here is the type's create stmt:
CREATE TYPE import_action AS ENUM
('Ignored',
'Inserted',
'Updated',
'Task created');
I just want to change 'Task created' to 'Aborted'. It seems like from the documentation, that the following should work:
ALTER TYPE import_action
RENAME ATTRIBUTE "Task created" TO "Aborted";
However, I get a msg:
********** Error **********
ERROR: relation "import_action" does not exist
SQL state: 42P01
But, it clearly does exist.
The type is currently being used by more than one table.
I'm being to think that there must not be a way to do this. I've tried the dialog for the type in pgAdminIII, but there is no way that I can see to rename the it there. (So, either a strong hint that I can't do it, or - I'm hoping - a small oversight be the developer that created that dialog)
If I can't do this in one statment? Then what do I need to do? Will I have to write a script to add the item, update all of the records to new value, then drop the old item? Will that even work?
It's seems like this should be a simple thing. As I understand it, the records are just storing a reference to the type and item. I don't think they are actually store the text value that I have given it. But, maybe I'm wrong here as well.
In PostgreSQL version 10, the ability to rename the labels of an enum has been added as part of the ALTER TYPE syntax:
ALTER TYPE name RENAME VALUE 'existing_enum_value' TO 'new_enum_value'
Update: For PostgreSQL version 10 or later, see the top-voted answer.
Names of enum values are called labels, attributes are something different entirely.
Unfortunately changing enum labels is not simple, you have to muck with the system catalog:
http://www.postgresql.org/docs/9.1/static/catalog-pg-enum.html
UPDATE pg_enum SET enumlabel = 'Aborted'
WHERE enumlabel = 'Task created' AND enumtypid = (
SELECT oid FROM pg_type WHERE typname = 'import_action'
)
The query in the accepted answer doesn't take into account schema names. Here's a safer (and simpler) one, based on http://tech.valgog.com/2010/08/alter-enum-in-postgresql.html
UPDATE pg_catalog.pg_enum
SET enumlabel = 'NEW_LABEL'
WHERE enumtypid = 'SCHEMA_NAME.ENUM_NAME'::regtype::oid AND enumlabel = 'OLD_LABEL'
RETURNING enumlabel;
Note that this requires the "rolcatupdate" (Update catalog directly) permission - even being a superuser is not enough.
It seems that updating the catalog directly is still the only way as of PostgreSQL 9.3.
There's a difference between types, attributes, and values. You can create an enum like this.
CREATE TYPE import_action AS ENUM
('Ignored',
'Inserted',
'Updated',
'Task created');
Having done that, you can add values to the enum.
ALTER TYPE import_action
ADD VALUE 'Aborted';
But the syntax diagram doesn't show any support for dropping or renaming a value. The syntax you were looking at was the syntax for renaming an attribute, not a value.
Although this design is perhaps surprising, it's also deliberate. From the pgsql-hackers mailing list.
If you need to modify the values used or want to know what the integer
is, use a lookup table instead. Enums are the wrong abstraction for
you.

How to alter Postgres table data based on its contents?

This is probably a super simple question, but I'm struggling to come up with the right keywords to find it on Google.
I have a Postgres table that has among its contents a column of type text named content_type. That stores what type of entry is stored in that row.
There are only about 5 different types, and I decided I want to change one of them to display as something else in my application (I had been directly displaying these).
It struck me that it's funny that my view is being dictated by my database model, and I decided I would convert the types being stored in my database as strings into integers, and enumerate the possible types in my application with constants that convert them into their display names. That way, if I ever got the urge to change any category names again, I could just change it with one alteration of a constant. I also have the hunch that storing integers might be somewhat more efficient than storing text in the database.
First, a quick threshold question of, is this a good idea? Any feedback or anything I missed?
Second, and my main question, what's the Postgres command I could enter to make an alteration like this? I'm thinking I could start by renaming the old content_type column to old_content_type and then creating a new integer column content_type. However, what command would look at a row's old_content_type and fill in the new content_type column based off of that?
If you're finding that you need to change the display values, then yes, it's probably a good idea not to store them in a database. Integers are also more efficient to store and search, but I really wouldn't worry about it unless you've got millions of rows.
You just need to run an update to populate your new column:
update table_name set content_type = (case when old_content_type = 'a' then 1
when old_content_type = 'b' then 2 else 3 end);
If you're on Postgres 8.4 then using an enum type instead of a plain integer might be a good idea.
Ideally you'd have these fields referring to a table containing the definitions of type. This should be via a foreign key constraint. This way you know that your database is clean and has no invalid values (i.e. referential integrity).
There are many ways to handle this:
Having a table for each field that can contain a number of values (i.e. like an enum) is the most obvious - but it breaks down when you have a table that requires many attributes.
You can use the Entity-attribute-value model, but beware that this is too easy to abuse and cause problems when things grow.
You can use, or refer to my implementation solution PET (Parameter Enumeration Tables). This is a half way house between between 1 & 2.