Is it safe to use inheritance for meta-data migration? - postgresql

Is it safe to create a template schema for metadata so that other schemas which contain data can inherit from it?
Advantage: Migrations will be seamless for multi tenant scenarios.
Disadvantages: ?
Example:
hello=# CREATE SCHEMA template;
hello=# CREATE TABLE template.cities (
name text,
population real,
altitude int;
hello=# CREATE SCHEMA us;
hello=# CREATE TABLE us.cities () INHERITS (template.cities);
hello=# \d us.cities;
Table "us.cities"
Column | Type | Collation | Nullable | Default
------------+--------------+-----------+----------+---------
name | text | | |
population | real | | |
altitude | integer | | |
Inherits: template.cities
hello=# CREATE SCHEMA eu;
hello=# CREATE TABLE eu.cities () INHERITS (template.cities);
hello=# \d eu.cities;
Table "eu.cities"
Column | Type | Collation | Nullable | Default
------------+--------------+-----------+----------+---------
name | text | | |
population | real | | |
altitude | integer | | |
Inherits: template.cities
hello=# ALTER TABLE cities ADD COLUMN state varchar(30);
hello=# \d us.cities;
Table "us.cities"
Column | Type | Collation | Nullable | Default
------------+-----------------------+-----------+----------+---------
name | text | | |
population | real | | |
altitude | integer | | |
state | character varying(30) | | |
Inherits: template.cities

I think inheritance is a good approach to this problem.
I can think of two down sides:
It is possible to create additional columns to the inheritance children. If you control DDL, you can probably prevent that.
You still have to create and modify indexes on all inheritance children individually.
If you are using PostgreSQL v11 or later, you could prevent both problems by using partitioning. The individual tables would then be partitions of the “template” table. This way, you can create indexes centrally by creating a partitioned index on the template table. The disadvantage (that may make this solution impossible) is that you need a partitioning key column in the table.

Related

Bulk update datatype of a column in all relevant tables

An example of some tables with the column I want to change.
+--------------------------------------+------------------+------+
| ?column? | column_name | data_type |
|--------------------------------------+------------------+------|
| x.articles | article_id | bigint |
| x.supplier_articles | article_id | bigint |
| x.purchase_order_details | article_id | bigint |
| y.scheme_articles | article_id | integer |
....
There are some 50 tables that have the column.
I want to change the article_id column from a numeric data type to a textual data type. It is found across several tables. Is there anyway to update them all at once ? Information schema is readonly so I cannot do an update on it. Other than writing inidividual alter statements for all the tables, is there a better way to do it ?

Postgres Alter Column Datatype & Update Values

I am new to writing postgres queries, I am stuck at a problem where the price is a string having $ prefix, I want to change the data type of column price to float and update the values by removing the $ prefix. Can someone help me do that?
bootcamp=# SELECT * FROM car;
id | make | model | price
----+---------+---------------------+-----------
1 | Ford | Explorer Sport Trac | $92075.96
2 | GMC | Safari | $81521.80
3 | Mercury | Grand Marquis | $64391.84
(3 rows)
bootcamp=# \d car
Table "public.car"
Column | Type | Collation | Nullable | Default
--------+-----------------------+-----------+----------+---------------------------------
id | bigint | | not null | nextval('car_id_seq'::regclass)
make | character varying(50) | | not null |
model | character varying(50) | | not null |
price | character varying(50) | | not null |
Thanks
You can cleanup the string while altering the table:
alter table car
alter column price type numeric using substr(price, 2)::numeric;
First you have to disable safe update mode to update without WHERE clause:
SET SQL_SAFE_UPDATES=0;
Then remove '$' from all rows:
UPDATE car SET price = replace(price, '$', '');
Then change the column type:
ALTER TABLE car ALTER COLUMN price TYPE your_desired_type;
If you want to enable safe update mode again:
SET SQL_SAFE_UPDATES=1;

postgREST can find relation

I'm trying to set up postgREST. Have been following the tutorial at http://postgrest.org/en/v5.1/tutorials/tut0.html. Here is what I see. First, the schemas:
entercarlson=# \dn
List of schemas
Name | Owner
--------+---------
api | carlson
public | carlson
Then a table:
carlson=# \d api.todos
Table "api.todos"
Column | Type | Collation | Nullable | Default
--------+--------------------------+-----------+----------+---------------------------------------
id | integer | | not null | nextval('api.todos_id_seq'::regclass)
done | boolean | | not null | false
task | text | | not null |
due | timestamp with time zone | | |
Indexes:
"todos_pkey" PRIMARY KEY, btree (id)
Finally, some data:
carlson=# select * from api.todos;
id | done | task | due
----+------+-------------------+-----
1 | f | finish tutorial 0 |
2 | f | pat self on back |
(2 rows)
But then I get this:
$ curl http://localhost:3000/todos
{"hint":null,"details":null,"code":"42P01","message":"relation
\"api.todos\" does not exist"}
Which is consistent with this:
carlson=# \d
Did not find any relations.
What am I doing wrong?
PS. I don't see which database this schema belongs to
Seems you're targeting the wrong database, check the db-uri config value and make sure this uri contains the api.todos table through psql.
Also, want to clarify that the search_path is modified by PostgREST on each request, so if you ALTER your connection user search_path it'll have no effect on the schemas PostgREST searches.

Postgres: unique on array and varchar column

I would like to ask if it is possible to set UNIQUE on array column - I need to check array items if are uniqued.
Secondly, I wish to have also second column to be included in this.
To imagine what I need, I'm including example: imagine, you have entries with domains and aliases. Column domain is varchar having main domain in it, aliases is array which can be empty. As logical, nothing in column domain can be in aliases as well as right opposite.
If there is any option how to do it, I would be glad for showing how. And the best will be to include help how to do it in sqlalchemy (table declaration, using in TurboGears).
Postgresql: 9.2
sqlalchemy: 0.7
UPDATE:
I have found, how to do multi-column unique in sqlalchemy, however it does not work on array:
client_table = Table('client', metadata,
Column('id', types.Integer, autoincrement = True, primary_key = True),
Column('name', types.String),
Column('domain', types.String),
Column('alias', postgresql.ARRAY(types.String)),
UniqueConstraint('domain', 'alias', name = 'domains')
)
Then desc:
wb=# \d+ client
Table "public.client"
Column | Type | Modifiers | Storage | Description
--------+---------------------+-----------------------------------------------------+----------+-------------
id | integer | not null default nextval('client_id_seq'::regclass) | plain |
name | character varying | | extended |
domain | character varying | not null | extended |
alias | character varying[] | | extended |
Indexes:
"client_pkey" PRIMARY KEY, btree (id)
"domains" UNIQUE CONSTRAINT, btree (domain, alias)
And select (after test insert):
wb=# select * from client;
id | name | domain | alias
----+-------+---------------+--------------------------
1 | test1 | www.test.com | {www.test1.com,test.com}
2 | test2 | www.test1.com |
3 | test3 | www.test.com |
Thanks in advance.
figure out how to do this in pure Postgresql syntax, then use DDL to emit it exactly.

Why psql can't find relation name for existing table?

Here' my current state.
Eonil=# \d+
List of relations
Schema | Name | Type | Owner | Size | Description
--------+------------+-------+-------+------------+-------------
public | TestTable1 | table | Eonil | 8192 bytes |
(1 row)
Eonil=# \d+ TestTable1
Did not find any relation named "TestTable1".
Eonil=#
What is the problem and how can I see the table definition?
Postgres psql needs escaping for capital letters.
Eonil=# \d+ "TestTable1"
So this works well.
Eonil=# \d+ "TestTable1"
Table "public.TestTable1"
Column | Type | Modifiers | Storage | Description
--------+------------------+-----------+----------+-------------
ID | bigint | not null | plain |
name | text | | extended |
price | double precision | | plain |
Indexes:
"TestTable1_pkey" PRIMARY KEY, btree ("ID")
"TestTable1_name_key" UNIQUE CONSTRAINT, btree (name)
Has OIDs: no
Eonil=#