Базы данных

Посмотрим список имеющихся баз:

postgres$ psql -l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
(3 rows)

Пока нас интересует только название, значение остальных полей рассмотрим позже.


Также можно посмотреть в самой базе данных:

=> SELECT datname, datistemplate, datallowconn, datconnlimit FROM pg_database;
  datname  | datistemplate | datallowconn | datconnlimit 
-----------+---------------+--------------+--------------
 postgres  | f             | t            |           -1
 template1 | t             | t            |           -1
 template0 | t             | f            |           -1
(3 rows)


Создание БД из шаблона

Подключимся к шаблонной базе template1.

=> \c template1
You are now connected to database "template1" as user "postgres".

Проверим, доступна ли функция digest, вычисляющая хеш-код текстовой строки.

=> SELECT digest('Hello, world!', 'md5');
ERROR:  function digest(unknown, unknown) does not exist
LINE 1: SELECT digest('Hello, world!', 'md5');
               ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Такой функции нет.


На самом деле digest определена в пакете pgcrypto. Установим его:

=> CREATE EXTENSION pgcrypto;
CREATE EXTENSION

Если бы расширение pgcrypto не было установлено с помощью make install, мы получили бы ошибку "ERROR: could not open extension control file..."


Теперь нам доступны функции, входящие в расширение pgcrypto. Например, можно вычислить MD5-дайджест:

=> SELECT digest('Hello, world!', 'md5');
               digest               
------------------------------------
 \x6cd3556deb0da54bca060b4c39479839
(1 row)


Чтобы шаблон можно было использовать для создания базы, к нему не должно быть активных подключений, поэтому отключимся от базы template1.

=> \c postgres
You are now connected to database "postgres" as user "postgres".

Для создания новой базы данных служит команда CREATE DATABASE:

=> CREATE DATABASE db;
CREATE DATABASE
=> \c db
You are now connected to database "db" as user "postgres".

Альтернативно можно было бы воспользоваться утилитой createdb.

=> SELECT datname, datistemplate, datallowconn, datconnlimit FROM pg_database;
  datname  | datistemplate | datallowconn | datconnlimit 
-----------+---------------+--------------+--------------
 postgres  | f             | t            |           -1
 template1 | t             | t            |           -1
 template0 | t             | f            |           -1
 db        | f             | t            |           -1
(4 rows)


Поскольку для создания по умолчанию используется шаблон template1, в ней также будет доступны функции пакета pgcrypto:

=> SELECT digest('Hello, world!', 'md5');
               digest               
------------------------------------
 \x6cd3556deb0da54bca060b4c39479839
(1 row)


Управление базами данных

Созданную базу данных можно переименовать (к ней не должно быть подключений):

=> \c postgres
You are now connected to database "postgres" as user "postgres".
=> ALTER DATABASE db RENAME TO appdb;
ALTER DATABASE
=> SELECT datname, datistemplate, datallowconn, datconnlimit FROM pg_database;
  datname  | datistemplate | datallowconn | datconnlimit 
-----------+---------------+--------------+--------------
 postgres  | f             | t            |           -1
 template1 | t             | t            |           -1
 template0 | t             | f            |           -1
 appdb     | f             | t            |           -1
(4 rows)


Можно изменить и другие параметры, например:

=> ALTER DATABASE appdb CONNECTION LIMIT 10;
ALTER DATABASE
=> SELECT datname, datistemplate, datallowconn, datconnlimit FROM pg_database;
  datname  | datistemplate | datallowconn | datconnlimit 
-----------+---------------+--------------+--------------
 postgres  | f             | t            |           -1
 template1 | t             | t            |           -1
 template0 | t             | f            |           -1
 appdb     | f             | t            |           10
(4 rows)


Размер базы данных

Размер базы данных можно узнать с помощью функции:

=> SELECT pg_database_size('appdb');
 pg_database_size 
------------------
          7222887
(1 row)

Чтобы не считать разряды, можно вывести размер в читаемом виде:

=> SELECT pg_size_pretty(pg_database_size('appdb'));
 pg_size_pretty 
----------------
 7054 kB
(1 row)

В этой базе данных еще нет пользовательских объектов (кроме расширения pgcrypto); фактически, это размер "пустой" базы.


Схемы

Список схем можно узнать командой psql (dn = describe namespace):

=> \dn
  List of schemas
  Name  |  Owner   
--------+----------
 public | postgres
(1 row)


Создадим новую схему:

=> \c appdb
You are now connected to database "appdb" as user "postgres".
=> CREATE SCHEMA app;
CREATE SCHEMA
=> \dn
  List of schemas
  Name  |  Owner   
--------+----------
 app    | postgres
 public | postgres
(2 rows)

Если теперь создать таблицу (и не указать имя схемы), в какую схему она попадет?


Надо посмотреть на путь поиска.

=> SHOW search_path;
   search_path   
-----------------
 "$user", public
(1 row)

Конструкция "$user" обозначает схему с тем же именем, что и имя текущего пользователя (в нашем случае - postgres). Поскольку такой схемы нет, она игнорируется.

Чтобы не думать над тем, какие схемы есть, каких нет, и какие не указаны явно, можно воспользоваться функцией:

=> SELECT current_schemas(true);
   current_schemas   
---------------------
 {pg_catalog,public}
(1 row)


Теперь создадим таблицу:

=> CREATE TABLE t(s text);
CREATE TABLE
=> INSERT INTO t VALUES ('Я - таблица t');
INSERT 0 1

Список таблиц можно получить командой \dt:

=> \dt
        List of relations
 Schema | Name | Type  |  Owner   
--------+------+-------+----------
 public | t    | table | postgres
(1 row)


Объект можно перемещать между схемами. Поскольку речь идет о логической организации, перемещение происходит только в системном каталоге; сами данные физически остаются на месте.

=> ALTER TABLE t SET SCHEMA app;
ALTER TABLE

Теперь к таблице t можно обращаться с явным указанием схемы:

=> SELECT * FROM app.t;
       s       
---------------
 Я - таблица t
(1 row)

Но если опустить имя схемы, таблица не будет найдена:

=> SELECT * FROM t;
ERROR:  relation "t" does not exist
LINE 1: SELECT * FROM t;
                      ^

Путь поиска

Установим путь поиска, например, так:

=> SET search_path = public, app;
SET

Теперь таблица будет найдена.

=> SELECT * FROM t;
       s       
---------------
 Я - таблица t
(1 row)


Здесь мы установили конфигурационный параметр на уровне сеанса (при переподключении значение пропадет). Устанавливать такое значение на уровне всего кластера тоже не правильно - возможно, этот путь нужен не всегда и не всем.

Но параметр можно установить и на уровне отдельной базы данных:

=> ALTER DATABASE appdb SET search_path = public, app;
ALTER DATABASE

Теперь он будет устанавливаться для всех новых подключений к БД appdb. Проверим:

=> \c appdb
You are now connected to database "appdb" as user "postgres".
=> SHOW search_path;
 search_path 
-------------
 public, app
(1 row)

=> SELECT current_schemas(true);
     current_schemas     
-------------------------
 {pg_catalog,public,app}
(1 row)


Временные таблицы и pg_temp

Создадим временную таблицу:

=> CREATE TEMP TABLE t(s text);
CREATE TABLE
=> \dt
          List of relations
  Schema   | Name | Type  |  Owner   
-----------+------+-------+----------
 pg_temp_3 | t    | table | postgres
(1 row)

Таблица создана в специальной схеме. Каждому сеансу выделяется отдельная "временная" схема, так что он может видеть только свои собственные временные таблицы.

Но куда пропала обычная таблица t?


Ответ дает развернутый путь поиска: в него теперь подставлена временная схема, и объект в ней "перекрывает" одноименный объект схемы app.

=> SELECT current_schemas(true);
          current_schemas          
-----------------------------------
 {pg_temp_3,pg_catalog,public,app}
(1 row)

=> INSERT INTO t VALUES ('Я - временная таблица');
INSERT 0 1

Тем не менее, к каждой из таблиц можно обращаться с явным указанием схемы. Для временной таблицы надо использовать псевдосхему pg_temp - она автоматически отображается в нужную схему pg_temp_N:

=> SELECT * FROM app.t;
       s       
---------------
 Я - таблица t
(1 row)

=> SELECT * FROM pg_temp.t;
           s           
-----------------------
 Я - временная таблица
(1 row)


В принципе, во временной схеме можно создавать не только таблицы.

=> CREATE VIEW v AS SELECT * FROM pg_temp.t;
NOTICE:  view "v" will be a temporary view
CREATE VIEW

Временные таблицы и данные в них могут иметь различные сроки жизни (в зависимости от указания ON COMMIT DELETE/PRESERVE/DROP). В любом случае при переподключении все объекты во временной схеме уничтожаются:

=> \c appdb
You are now connected to database "appdb" as user "postgres".
=> SELECT current_schemas(true);
     current_schemas     
-------------------------
 {pg_catalog,public,app}
(1 row)

=> SELECT * FROM pg_temp.v;
ERROR:  relation "pg_temp.v" does not exist
LINE 1: SELECT * FROM pg_temp.v;
                      ^
=> SELECT * FROM pg_temp.t;
ERROR:  relation "pg_temp.t" does not exist
LINE 1: SELECT * FROM pg_temp.t;
                      ^

Удаление объектов

Схему нельзя удалить, если в ней находятся какие-либо объекты:

=> DROP SCHEMA app;
ERROR:  cannot drop schema app because other objects depend on it
DETAIL:  table t depends on schema app
HINT:  Use DROP ... CASCADE to drop the dependent objects too.

Но можно удалить схему вместе со всеми ее объектами:

=> DROP SCHEMA app CASCADE;
NOTICE:  drop cascades to table t
DROP SCHEMA

Базу данных можно удалить, если к ней нет активных подключений.

=> \conninfo
You are connected to database "appdb" as user "postgres" via socket in "/tmp" at port "5432".
=> \c postgres
You are now connected to database "postgres" as user "postgres".
=> DROP DATABASE appdb;
DROP DATABASE

Конец демонстрации.