Mirajv1.0
EN

40. PostgreSQL: Catalog and tools

This chapter describes what a client sees of a MIRAJ instance in the PostgreSQL language: the pg_catalog catalog, information_schema and the SHOW statements, then the clients and drivers (psql, psycopg2, pgjdbc, Npgsql, ODBC, the miraj Python library), MIRAJ's tools on such an instance, and backup.

The examples use the ventes schema from getting started (tables client, article, commande, ligne_commande), completed with a view v_total_commande, a sequence seq_facture, a function ttc and a trigger trg_maj_stock.

40.1 Model: one database, one schema per MIRAJ database#

PostgreSQL conceptIn MIRAJ
database (pg_database, current_database())a single one, miraj (OID 16384, UTF8 encoding)
schema (pg_namespace)a MIRAJ database; pg_catalog and information_schema are added to them
current schema (current_schema, search_path)the session's current database
publicnot a schema: in a name (public.client), an alias of the current database
object ownerroot (OID 10)
SELECT oid, nspname FROM pg_namespace ORDER BY nspname;
    oid     |      nspname
------------+--------------------
      13183 | information_schema
  689122271 | miraj
         11 | pg_catalog
 1505511691 | ventes
(4 lignes)

The miraj schema is MIRAJ's system database: only root sees it. An ordinary account sees only the schemas on which it has a privilege (the app account from the getting started chapter sees information_schema, pg_catalog and ventes).

SELECT current_database(), current_schema, current_schemas(true), current_schemas(false);
 current_database() | current_schema | current_schemas(true) | current_schemas(false)
--------------------+----------------+-----------------------+------------------------
 miraj              | ventes         | {pg_catalog,ventes}   | {ventes}
(1 ligne)

Object identifiers (OIDs). Base types and catalog tables keep PostgreSQL's OIDs (int4 23, text 25, numeric 1700, pg_class 1259). The objects you create receive an OID computed from their kind, schema and name: it does not change across restarts, but renaming an object changes its OID.

Name resolution. pg_catalog comes before any schema, as in PostgreSQL: SELECT * FROM pg_class reads the catalog table, without a prefix. A table in another schema is named schema.table.

Views on the catalog. A view can read the tables of pg_catalog and information_schema, alone, joined, through another view or through a WITH expression:

CREATE VIEW v_tables AS
SELECT n.nspname AS schema, c.relname AS nom
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname = 'ventes';

Its query is replayed on every read, on behalf of the account that reads it: it follows the catalog without delay (a table you create appears at once) and shows each account only what the catalog shows it. No result is kept, even for a view created by CREATE CACHED VIEW; REFRESH VIEW has no effect on it. A view on pg_authid or pg_shadow remains reserved for whoever administers accounts, at creation as at read time (42501).

Read-only. The catalog is rebuilt on every statement that reads it; nothing can be written to it:

INSERT INTO pg_class (relname) VALUES ('x');
ERREUR 42501 : permission denied for schema pg_catalog

40.2 The pg_catalog catalog#

Tables and views present#

The columns are those of PostgreSQL 16, in the same order and with the same types.

Table or viewContent
pg_namespacethe schemas visible to the account, pg_catalog, information_schema
pg_databasethe miraj database
pg_classtables (relkind = r), views (v), indexes (i), sequences (S), and the catalog tables
pg_attribute, pg_attrdefcolumns of tables and views (type, attnotnull, identity attidentity = d for a serial or BY DEFAULT, a for GENERATED ALWAYS, generated column), default values
pg_index, pg_indexesindexes: primary key <table>_pkey, uniqueness, secondary indexes (btree), full-text (gin), spatial (gist), vector (hnsw)
pg_constraintconstraints p (primary key), u (uniqueness), f (foreign key), c (CHECK)
pg_type, pg_enum, pg_rangebase types and their arrays (92 types), one enumerated type per ENUM column; pg_range empty
pg_proc, pg_languagefunctions (prokind = f) and procedures (p); languages internal, c, sql, plpgsql
pg_triggertriggers, under their PostgreSQL name
pg_sequence, pg_sequencessequences (CREATE SEQUENCE); pg_sequences presents them with their schema, owner and bounds
pg_views, pg_tables, pg_matviewsusual views; pg_matviews empty
pg_descriptioncomments on tables and columns (COMMENT ON)
pg_roles, pg_user, pg_authid, pg_shadow, pg_auth_membersone role per account, its attributes (rolsuper, rolcreatedb, rolcreaterole) and the granted roles; password always masked; pg_authid and pg_shadow reserved for whoever administers accounts (42501, permission denied for table pg_authid)
pg_settingsparameters announced to clients and session variables (209 rows)
pg_stat_activityopen sessions
pg_am, pg_collation, pg_tablespace, pg_extensionaccess methods (heap, btree, hash, gist, gin, hnsw), collations default / C / POSIX, pg_default / pg_global, extension vector 0.8.0pg_inherits, pg_partitioned_table, pg_depend, pg_locks, pg_stat_user_tables, pg_shdescription, pg_policy, pg_publication, pg_publication_rel, pg_publication_namespace, pg_statistic_ext, pg_rewrite, pg_foreign_data_wrapper, pg_foreign_server, pg_foreign_table, pg_opclass, pg_event_trigger, pg_init_privspresent and empty, so that the queries of clients that join them run

The catalog arrays (conkey, proargnames...) are rendered as arrays ({1,2}), indkey as int2vector (1 2).

Extensions. vector is the only row of pg_extension and does not need to be installed. CREATE EXTENSION [IF NOT EXISTS] name [WITH] [SCHEMA s] [VERSION v] [CASCADE] and DROP EXTENSION [IF EXISTS] name [, ...] are accepted with no effect for plpgsql, pgcrypto, uuid-ossp, unaccent, pg_trgm, fuzzystrmatch, btree_gin, btree_gist and vector, whose functions (gen_random_bytes, uuid_generate_v4, unaccent, similarity, levenshtein...) are always available (36. SQL functions): an installation script written for PostgreSQL runs unchanged.

Objects of a schema#

SELECT c.relname, c.relkind, c.relnatts, c.reltuples
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'ventes'
ORDER BY c.relkind, c.relname;
       relname       | relkind | relnatts | reltuples
---------------------+---------+----------+-----------
 article_pkey        | i       |        1 |       3.0
 client_pkey         | i       |        1 |       3.0
 code                | i       |        1 |       3.0
 commande_pkey       | i       |        1 |       3.0
 email               | i       |        1 |       3.0
 idx_commande_date   | i       |        1 |       3.0
 ligne_commande_pkey | i       |        2 |       4.0
 article             | r       |        5 |       3.0
 client              | r       |        6 |       3.0
 commande            | r       |        4 |       3.0
 ligne_commande      | r       |        4 |       4.0
 seq_facture         | S       |        3 |       1.0
 v_total_commande    | v       |        0 |       0.0
(13 lignes)

The names are MIRAJ's: the primary key is called <table>_pkey, a column UNIQUE constraint takes the name of its column (email, code), a foreign key is <table>_ibfk_<n>, an unnamed CHECK constraint is CONSTRAINT_<n>, numbered within its table (article and ligne_commande each have their own CONSTRAINT_1).

Columns of a table#

SELECT a.attnum, a.attname, format_type(a.atttypid, a.atttypmod) AS type,
       a.attnotnull, a.attidentity, pg_get_expr(d.adbin, d.adrelid) AS defaut
FROM pg_attribute a
LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
WHERE a.attrelid = 'client'::regclass AND a.attnum > 0 AND NOT a.attisdropped
ORDER BY a.attnum;
 attnum | attname |            type             | attnotnull | attidentity |      defaut
--------+---------+-----------------------------+------------+-------------+-------------------
      1 | id      | integer                     | t          | d           |
      2 | nom     | character varying(80)       | t          |             |
      3 | email   | character varying(120)      | t          |             |
      4 | ville   | text                        | f          |             |
      5 | actif   | boolean                     | t          |             | true
      6 | cree_le | timestamp without time zone | f          |             | CURRENT_TIMESTAMP
(6 lignes)

In pg_attribute, a serial column appears as an identity column (attidentity = d), with no default value in pg_attrdef; a GENERATED ALWAYS AS IDENTITY column carries a. information_schema.columns, for its part, presents it as PostgreSQL does: nextval('client_id_seq'::regclass) (40.6).

Indexes and constraints#

SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'commande' ORDER BY indexname;
     indexname     |                                 indexdef
-------------------+---------------------------------------------------------------------------
 commande_pkey     | CREATE UNIQUE INDEX commande_pkey ON ventes.commande USING btree (id)
 idx_commande_date | CREATE INDEX idx_commande_date ON ventes.commande USING btree (passee_le)
(2 lignes)
SELECT conname, contype, conkey, confrelid::regclass AS cible, pg_get_constraintdef(oid) AS definition
FROM pg_constraint
WHERE conrelid = 'ligne_commande'::regclass
ORDER BY conname;
        conname        | contype | conkey |  cible   |                             definition
-----------------------+---------+--------+----------+---------------------------------------------------------------------
 CONSTRAINT_1          | c       |        | 0        | CHECK (("quantite" > 0))
 ligne_commande_ibfk_1 | f       | {1}    | commande | FOREIGN KEY (commande_id) REFERENCES commande(id) ON DELETE CASCADE
 ligne_commande_ibfk_2 | f       | {2}    | article  | FOREIGN KEY (article_id) REFERENCES article(id)
 ligne_commande_pkey   | p       | {1,2}  | 0        | PRIMARY KEY (commande_id, article_id)
(4 lignes)

For a constraint that is not a foreign key, confrelid::regclass returns 0.

Primary key columns of a table:

SELECT a.attname
FROM pg_class c
JOIN pg_index i ON i.indrelid = c.oid AND i.indisprimary
JOIN pg_attribute a ON a.attrelid = c.oid AND a.attnum = ANY (i.indkey)
WHERE c.relname = 'ligne_commande'
ORDER BY a.attnum;
   attname
-------------
 commande_id
 article_id
(2 lignes)

Tables that reference a table:

SELECT conrelid::regclass AS table_enfant, conname, pg_get_constraintdef(oid)
FROM pg_constraint
WHERE contype = 'f' AND confrelid = 'client'::regclass;
 table_enfant |     conname     |           pg_get_constraintdef(oid)
--------------+-----------------+-----------------------------------------------
 commande     | commande_ibfk_1 | FOREIGN KEY (client_id) REFERENCES client(id)
(1 ligne)

Views, sequences, routines, triggers#

SELECT pg_get_viewdef('v_total_commande'::regclass);
 select "o"."id", "o"."client_id", sum(("l"."quantite" * "l"."prix_unit")) as "montant" from "commande" as "o" join "ligne_commande" as "l" on ("l"."commande_id" = "o"."id") group by "o"."id","o"."client_id";

The definition is the one MIRAJ keeps: the query is rewritten, identifiers always quoted. pg_get_viewdef returns it preceded by a space and ending with ;, as PostgreSQL does; pg_views.definition and information_schema.views.view_definition return the same text, without the space or the ;.

SELECT seqrelid::regclass AS sequence, seqstart, seqincrement, seqmin, seqmax, seqcache, seqcycle
FROM pg_sequence;
  sequence   | seqstart | seqincrement | seqmin |       seqmax        | seqcache | seqcycle
-------------+----------+--------------+--------+---------------------+----------+----------
 seq_facture |     1000 |            1 |      1 | 9223372036854775806 |     1000 | f
(1 ligne)
SELECT p.proname, p.prokind, l.lanname, p.provolatile,
       pg_get_function_arguments(p.oid) AS arguments, pg_get_function_result(p.oid) AS resultat
FROM pg_proc p
JOIN pg_language l ON l.oid = p.prolang
JOIN pg_namespace n ON n.oid = p.pronamespace
WHERE n.nspname = 'ventes';
  proname  | prokind | lanname | provolatile |                    arguments                     |   resultat
-----------+---------+---------+-------------+--------------------------------------------------+---------------
 maj_stock | f       | plpgsql | v           |                                                  | trigger
 ttc       | f       | plpgsql | i           | ht numeric(10,2), taux numeric(4,2) DEFAULT 0.19 | numeric(10,2)
(2 lignes)
SELECT pg_get_functiondef('ttc'::regproc);
CREATE OR REPLACE FUNCTION ventes.ttc(ht numeric(10,2), taux numeric(4,2) DEFAULT 0.19)
 RETURNS numeric(10,2)
 LANGUAGE plpgsql
 IMMUTABLE
AS $function$
BEGIN
  RETURN round(ht * (1 + taux), 2);
END;
$function$
SELECT tgname, tgrelid::regclass AS table, tgfoid::regproc AS fonction, pg_get_triggerdef(oid) AS definition
FROM pg_trigger;
    tgname     |     table      | fonction  |                                                  definition
---------------+----------------+-----------+--------------------------------------------------------------------------------------------------------------
 trg_maj_stock | ligne_commande | maj_stock | CREATE TRIGGER trg_maj_stock AFTER INSERT ON ventes.ligne_commande FOR EACH ROW EXECUTE FUNCTION maj_stock()
(1 ligne)

Routines and triggers are described in chapter 39.

Comments#

COMMENT ON applies to a table or a column; IS NULL removes the comment. A column comment redefines the column: the table is rewritten.

COMMENT ON TABLE client IS 'Clients de la boutique';
COMMENT ON COLUMN client.email IS 'Adresse de contact, unique';
SELECT obj_description('client'::regclass, 'pg_class') AS de_table,
       col_description('client'::regclass, 3) AS de_colonne;
        de_table        |         de_colonne
------------------------+----------------------------
 Clients de la boutique | Adresse de contact, unique
(1 ligne)

Roles#

SELECT rolname, rolsuper, rolcanlogin, rolpassword FROM pg_roles ORDER BY rolname;
 rolname | rolsuper | rolcanlogin | rolpassword
---------+----------+-------------+-------------
 app     | f        | t           | ********
 root    | t        | t           | ********
(2 lignes)

One role per account name (without its host). The password is never returned: ******** in pg_roles and pg_user, NULL in pg_authid. rolsuper, rolcreatedb and rolcreaterole follow the account's global privileges, pg_auth_members the granted roles, and pg_has_role queries them (see 38.2 and 38.5). Detailed privileges are not in the catalog (relacl is NULL): SHOW GRANTS FOR account returns them (see 38).

Information functions#

FunctionsResult
version()PostgreSQL 16.4 (MIRAJ 1.0.0) on x86_64-windows, 64-bit (aarch64-macos, x86_64-linux... depending on the server's platform)
current_database(), current_catalogmiraj
current_schema, current_schemas(bool)current schema; {pg_catalog,ventes}
current_user, current_role, session_user, pg_get_userbyid(oid)account name, without its host
pg_backend_pid()session number (the one in pg_stat_activity.pid and in KILL)
pg_postmaster_start_time(), pg_conf_load_time()time the server started, time its configuration was loaded
inet_server_addr(), inet_client_addr()addresses of the server and of the client of the connection (127.0.0.1)
pg_reload_conf()t
txid_current(), pg_current_xact_id(), txid_current_if_assigned(), txid_current_snapshot()number of the current transaction (NULL for txid_current_if_assigned() outside a write), snapshot (626841:626841:)
pg_advisory_lock(n), pg_try_advisory_lock(n), pg_advisory_unlock(n), pg_advisory_xact_lock(n), pg_try_advisory_xact_lock(n)advisory locks, session-level or transaction-level; the try forms return t or f
pg_column_size(x), pg_input_is_valid(text, type)size of a value in bytes (pg_column_size('abc'::text): 4); pg_input_is_valid('x', 'integer'): f
pg_terminate_backend(pid [, timeout]), pg_cancel_backend(pid)closes session pid, interrupts its running query: t; f with a warning if pid is unknown
format_type(oid, typmod), pg_typeof(x)character varying(80), numeric(10,2), timestamp without time zone...
pg_get_indexdef(oid), pg_get_constraintdef(oid), pg_get_viewdef(oid), pg_get_expr(text, oid)definitions, in the PostgreSQL language
pg_get_functiondef(oid), pg_get_function_arguments(oid), pg_get_function_identity_arguments(oid), pg_get_function_result(oid), pg_get_triggerdef(oid)routines and triggers (pg_get_function_identity_arguments: ht numeric(10,2), taux numeric(4,2), without default values)
obj_description(oid [, catalog]), col_description(oid, n), shobj_description(oid, catalog)comments
to_regclass(name), to_regtype(name), to_regnamespace(name), to_regproc(name), to_regrole(name)OID, or NULL if the name does not exist
pg_table_is_visible(oid), pg_function_is_visible(oid), pg_type_is_visible(oid)t for an object of the current schema or of pg_catalog
pg_relation_filepath(relation)path of the relation in PostgreSQL style (base/16384/1075441615)
has_table_privilege([role,] table, privilege), has_column_privilege([role,] table, column, privilege), has_any_column_privilege(...), has_schema_privilege([role,] schema, privilege), has_function_privilege([role,] function, 'EXECUTE')actual privileges of the session's account, or of another account for whoever administers accounts (see 38.5); an object the account does not see returns 42P01 / 3F000 / 42883; has_database_privilege, has_sequence_privilege, has_type_privilege: always t
pg_has_role([account,] role, 'MEMBER' | 'USAGE' | 'SET')whether the role is granted to the account (see 38.5); unknown role: 42704
pg_relation_size(relation), pg_total_relation_size(relation), pg_table_size(relation), pg_indexes_size(relation), pg_database_size(name), pg_size_pretty(n)sizes in bytes; the relation is given by its OID or its name (pg_relation_size('client'), 'ventes.client'), an unknown name returns 42P01 (relation "absente" does not exist); pg_indexes_size returns the estimated size of the table's indexes, which pg_total_relation_size adds to the table's size (client: 456 + 276 = 732); pg_size_pretty(20480::bigint) returns 20 kB
current_setting(name [, missing_ok])value of a parameter; unknown: 42704 (unrecognized configuration parameter "nexiste"), or NULL with true
pg_encoding_to_char(n), quote_ident(t), quote_literal(t), quote_nullable(t)UTF8; "Mon Nom", 'l''été', NULL
pg_is_in_recovery()f

Name conversions:

SyntaxResult
'client'::regclassOID of the table in the current schema ('ventes.client'::regclass for another schema); 42P01 (relation "absente" does not exist) if it does not exist
c.oid::regclass, conrelid::regclassname of the table
'int4'::regtype::oid23; atttypid::regtype returns the name (integer, numeric)
'ttc'::regproc, tgfoid::regprocOID of a routine, its name

40.3 information_schema and SHOW statements#

information_schema#

information_schema is MIRAJ's own, presented in the PostgreSQL language in the way PostgreSQL does: the catalog columns (table_catalog, constraint_catalog, catalog_name...) carry the name of the instance's PostgreSQL database, miraj, the one current_database() returns (the MIRAJ language reads def there, like the reference). Its usual tables answer portable queries, with a few differences to know about:

ViewNotes
schematacatalog_name is miraj; also lists miraj, performance_schema and sys
tablestable_catalog = miraj; table_type: BASE TABLE, VIEW, SEQUENCE
columnsdata_type carries the standard name (integer, character varying, timestamp without time zone, boolean, numeric); a serial column is presented as in PostgreSQL: column_default = nextval('<table>_<column>_seq'::regclass), is_identity = NO; a GENERATED ... AS IDENTITY column carries is_identity = YES, identity_generation ALWAYS or BY DEFAULT and a NULL column_default; udt_name carries the type's internal name (int4, int8, varchar, bpchar, bool, timestamptz, float8...; <table>_<column>_enum for an enumerated type, like pg_type.typname), udt_schema its schema (pg_catalog, or the table's for an enumerated type), udt_catalog is miraj
routinesdata_type carries the standard name (trigger, void, record for these pseudo-types); type_udt_name the internal name of the result type, type_udt_schema is pg_catalog, type_udt_catalog miraj (NULL for a procedure)
table_constraintsthe primary key is called <table>_pkey there, as in pg_constraint; carries table_catalog
constraint_column_usagecolumns each constraint applies to: those of a primary key or UNIQUE, the referenced columns of a foreign key, those a CHECK cites; PostgreSQL-language view only (absent from an instance in the MIRAJ language, where a view that reads it stays readable)
key_column_usagealso carries referenced_table_name and referenced_column_name for a foreign key
referential_constraintsupdate_rule, delete_rule (NO ACTION, CASCADE...)
check_constraintscheck_clause is written in the PostgreSQL language, identifiers in double quotes (("prix" >= 0))
viewsview_definition in the PostgreSQL language
routinesdata_type carries the standard name (integer, text, character varying...), trigger for a trigger function, void, record
triggersa trigger written in the PostgreSQL language carries its name there, one row per event, action_statement = EXECUTE FUNCTION f(...) and its WHEN condition in action_condition
sequencessequences created by CREATE SEQUENCE
domainsview present, empty
schema_privileges, table_privilegesgranted privileges (grantee = 'app'@'%')

The names of these views and their columns are written indifferently in lower case or upper case.

SELECT constraint_name, table_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'ventes' AND table_name IN ('client', 'commande')
ORDER BY table_name, constraint_name;
 constraint_name | table_name | constraint_type
-----------------+------------+-----------------
 client_pkey     | client     | PRIMARY KEY
 email           | client     | UNIQUE
 commande_ibfk_1 | commande   | FOREIGN KEY
 commande_pkey   | commande   | PRIMARY KEY
(4 lignes)
SELECT constraint_name, table_name, column_name, referenced_table_name, referenced_column_name
FROM information_schema.key_column_usage
WHERE table_schema = 'ventes' AND referenced_table_name IS NOT NULL
ORDER BY table_name, constraint_name;
    constraint_name    |   table_name   | column_name | referenced_table_name | referenced_column_name
-----------------------+----------------+-------------+-----------------------+------------------------
 commande_ibfk_1       | commande       | client_id   | client                | id
 ligne_commande_ibfk_1 | ligne_commande | commande_id | commande              | id
 ligne_commande_ibfk_2 | ligne_commande | article_id  | article               | id
(3 lignes)

For a tool written for PostgreSQL, prefer pg_catalog, which is more faithful; information_schema suits simple queries (list of tables, of columns).

SHOW statements#

SHOW name reads a parameter, as in PostgreSQL; SHOW ALL returns all parameters (name, setting, description).

SHOW server_version;          -- 16.4
SHOW search_path;             -- ventes
SHOW sql_mode;                -- POSTGRESQL,STRICT_TRANS_TABLES
SHOW transaction_isolation;   -- read committed
SHOW statement_timeout;       -- 5min

MIRAJ's SHOW statements remain available, as an extension:

StatementReturns
SHOW DATABASESthe schemas (MIRAJ databases)
SHOW TABLES, SHOW FULL TABLEStables, views and sequences of the current schema, with their kind
SHOW CREATE TABLE t, SHOW CREATE VIEW v, SHOW CREATE SEQUENCE s, SHOW CREATE FUNCTION fthe definition, written in the PostgreSQL language
SHOW COLUMNS FROM t, DESCRIBE t, SHOW INDEX FROM t, SHOW TABLE STATUSdescription in MIRAJ style: MIRAJ types (int(11), longtext, tinyint(1), datetime(6)), primary key PRIMARY
SHOW TRIGGERSMIRAJ columns; a trigger written in the PostgreSQL language carries its name there and, for Statement, EXECUTE FUNCTION f(...) (one row per event)
SHOW PROCESSLISTsessions (see 40.7)
SHOW GRANTS FOR accountprivileges of an account
SHOW VARIABLES LIKE '...', SHOW STATUS LIKE '...'server variables and counters
SHOW CREATE TABLE commande;
CREATE TABLE "commande" (
  "id" integer GENERATED BY DEFAULT AS IDENTITY (START WITH 4) NOT NULL,
  "client_id" integer NOT NULL,
  "passee_le" date NOT NULL DEFAULT CURRENT_DATE,
  "etat" character varying(12) NOT NULL DEFAULT 'ouverte'::character varying,
  PRIMARY KEY ("id"),
  CONSTRAINT "commande_ibfk_1" FOREIGN KEY ("client_id") REFERENCES "client" ("id")
);
CREATE INDEX "idx_commande_date" ON "commande" ("passee_le")

START WITH gives the number the next row will receive: a script replayed elsewhere resumes the numbering where it left off.

EXPLAIN query returns MIRAJ's plan, in its own layout (columns id, select_type, table, type, possible_keys, key, key_len, ref, rows, Extra):

EXPLAIN SELECT * FROM client WHERE id = 1;
 id | select_type | table  |  type  | possible_keys |   key   | key_len |  ref  | rows | Extra
----+-------------+--------+--------+---------------+---------+---------+-------+------+-------
  1 | SIMPLE      | client | eq_ref | PRIMARY,email | PRIMARY |         | const |    1 |
(1 ligne)

40.4 psql#

$env:PGPASSWORD = "password"
psql -h 127.0.0.1 -p 7007 -U root -d ventes
psql "host=server.example port=7007 user=root dbname=ventes sslmode=require"

The -d parameter (or dbname) names the current schema (see 31.2). The server works only in UTF-8: on Windows, use chcp 65001 or PGCLIENTENCODING=UTF8.

The description commands read pg_catalog:

CommandShows
\lthe single miraj database
\dnthe schemas, that is, the MIRAJ databases
\dt, \dv, \di, \ds, \dtables, views, indexes, sequences of the current schema; \dt ventes.* for another schema
\d table, \d+ tablecolumns (PostgreSQL types, default values, identity), indexes, constraints, foreign keys, triggers
\duaccounts
\df, \df+, \sf functionroutines, text of a function
\dxvector extension
\dptables, without detailed privileges
\xexpanded display
\copy t from 'file.csv' with (format csv, header), \copy t to 'output.txt'loads or writes a file on the client machine (see 34)

These commands send ordinary queries on the catalog. The one behind \d without an argument (tables, views and sequences of the current schema) returns here:

 Schema |       Name       |   Type   | Owner
--------+------------------+----------+-------
 ventes | article          | table    | root
 ventes | client           | table    | root
 ventes | commande         | table    | root
 ventes | ligne_commande   | table    | root
 ventes | seq_facture      | sequence | root
 ventes | v_total_commande | view     | root
(6 lignes)

A query typed or passed with -c can contain several statements; each returns its result and its tag. Ctrl-C cancels the running query (57014). psql -f script.sql runs a script, for example a miraj-dump export (40.9).

Not supported: \gdesc. The catalog queries are those of psql 10 to 18; a version that changed their text may receive a syntax error on a \d command.

40.5 Drivers and libraries#

Any PostgreSQL driver connects to the server's port, with the name of a schema as the database. MIRAJ's ODBC driver and the miraj Python library also speak both languages.

DriverConnection
psycopg2 (Python)psycopg2.connect(host=..., port=7007, user=..., password=..., dbname="ventes")
pgjdbc (Java)jdbc:postgresql://server:7007/ventes
Npgsql (.NET)Host=server;Port=7007;Username=...;Password=...;Database=ventes
MIRAJ ODBC DriverDRIVER={MIRAJ ODBC Driver};SERVER=server;PORT=7007;UID=...;PWD=...;DATABASE=ventes
miraj Python librarymiraj.connect(host=..., port=7007, user=..., password=..., dbname="ventes")

Parameters announced at connection: server_version = 16.4, server_encoding = client_encoding = UTF8, DateStyle = ISO, MDY, integer_datetimes = on, standard_conforming_strings = on, TimeZone = the server's time zone. Any other client_encoding is refused (0A000).

psycopg2#

import psycopg2, psycopg2.extras

conn = psycopg2.connect(host="127.0.0.1", port=7007, user="app",
                        password="secret123", dbname="ventes")
cur = conn.cursor()

cur.execute("SELECT code, prix, stock FROM article WHERE prix > %s ORDER BY id", (2,))
for code, prix, stock in cur:
    print(code, prix, stock)          # CAH-96 2.50 200 / CLA-A4 4.90 80

cur.execute("INSERT INTO client (nom, email) VALUES (%(nom)s, %(email)s) RETURNING id",
            {"nom": "Nadia Haddad", "email": "[email protected]"})
identifiant = cur.fetchone()[0]
conn.commit()
TopicBehavior
Parameters%s and %(name)s, substituted by psycopg2; cursor.mogrify writes 'l''été', NULL, '2026-01-02'::date
Types receivedint, str, bool, Decimal (numeric), float, date, time, datetime (with a time zone for a timestamptz), dict / list (json, jsonb), memoryview (bytea), str (uuid)
Types sentpsycopg2.extras.Json(...) for a jsonb column, psycopg2.Binary(...) for a bytea, Decimal, dates and times
Tagcursor.statusmessage: SELECT 3, INSERT 0 1, UPDATE 2...; cursor.rowcount
Generated identifierINSERT ... RETURNING id; cursor.lastrowid is 0
Transactionsopened at the first statement; commit(), rollback(), autocommit, with conn:; set_session(isolation_level=..., readonly=True) (a write then receives 25006)
Errorspsycopg2 classes from the SQLSTATE: UndefinedTable (42P01), UniqueViolation (23505), InFailedSqlTransaction (25P02), QueryCanceled (57014)...; e.pgcode, e.diag.message_primary
NoticesRAISE NOTICE from a function: conn.notices (NOTICE: bonjour 42)
Batchesexecutemany, psycopg2.extras.execute_values, execute_batch
Named cursorconn.cursor(name="grand"): server-side DECLARE ... CURSOR, read by fetchmany; inside a transaction
Dictionary cursorsRealDictCursor, DictCursor
Copiescopy_from, copy_to, copy_expert (text and CSV)
Cancellationconn.cancel() from another thread: the query fails with 57014; so does SET statement_timeout TO 1000
Asynchronous modepsycopg2.connect(async_=1, ...) with wait_select

Bulk load and export:

import io

donnees = io.StringIO("GOM-01\tGomme blanche\t0.80\t300\nREG-30\tRègle 30 cm\t1.90\t120\n")
cur.copy_from(donnees, "article", columns=("code", "libelle", "prix", "stock"))

sortie = io.StringIO()
cur.copy_expert("COPY (SELECT code, libelle, prix FROM article ORDER BY id) "
                "TO STDOUT WITH (FORMAT csv, HEADER)", sortie)
print(sortie.getvalue())
code,libelle,prix
STY-01,Stylo bille,1.30
CAH-96,Cahier 96 pages,2.50
CLA-A4,Classeur A4,4.90
GOM-01,Gomme blanche,0.80
REG-30,Règle 30 cm,1.90

Good to know:

  • cursor.callproc("f", ...) calls a function: psycopg2 sends SELECT * FROM f(...), which returns one row of one column. For a procedure, write cur.execute("CALL p(%s)", ...).
  • The connection's options (options="-c statement_timeout=5000", forms -c name=value and --name=value, a backslash before a space in the value) are applied before the first query, as so many SETs: SHOW statement_timeout returns 5s, and RESET statement_timeout as well as RESET ALL return to it. An unknown option, a refused value or a server startup parameter (shared_buffers...) refuses the connection (FATAL, 42704, 22023, 55P02...), as PostgreSQL does. The other command-line options of the PostgreSQL server (-d, -S...) are refused (42601).
  • The parameters that a driver writes in the startup message itself (pgjdbc and Npgsql: client_encoding, DateStyle, TimeZone, extra_float_digits, search_path, application_name...) are set the same way, as starting values of the session. There, an unknown name or a value the server cannot honor (client_encoding other than UTF8, DateStyle other than ISO, unknown time zone) is ignored without refusing the connection.
  • An execute call that contains several queries returns only the result of the last one, as with PostgreSQL.

pgjdbc and Npgsql#

These drivers prepare their queries: they use the extended protocol (parameters $1, $2..., values in text or binary), which the server supports. Chapter 15 gives the details and the list of functions tried (15.10.4).

DriverSupported
pgjdbc 42.7typed PreparedStatement, server-side prepared statements, batches (executeBatch), setFetchSize, isolation levels, getGeneratedKeys (RETURNING), CopyManager in text and CSV, DatabaseMetaData (getSchemas, getTables, getColumns, getPrimaryKeys, getImportedKeys, getExportedKeys, getIndexInfo, getTypeInfo...), updatable ResultSet
Npgsql 10Open (types loaded from pg_type), typed parameterized commands, reused prepared command, transactions, NpgsqlBatch, cancellation, ExecuteReader on INSERT ... RETURNING, BeginTextImport / BeginTextExport, GetSchema (Tables, Columns, Indexes, ForeignKeys...)
DBeavergoes through pgjdbc: navigator (schemas, tables, columns, keys, indexes) and row editing
await using var conn = new NpgsqlConnection("Host=serveur;Port=7007;Username=app;Password=…;Database=ventes");
await conn.OpenAsync();
await using var cmd = new NpgsqlCommand("SELECT libelle, prix FROM article WHERE prix > $1", conn);
cmd.Parameters.Add(new NpgsqlParameter { Value = 2.0m, NpgsqlDbType = NpgsqlDbType.Numeric });

Common limits: no binary COPY (Npgsql's BeginBinaryImport), no COPY in a prepared statement.

MIRAJ ODBC driver#

MIRAJ ODBC Driver (installation and data sources: chapter 28) recognizes the instance's language at connection time; PROTOCOL=postgresql in the string forces it.

import pyodbc

cn = pyodbc.connect("DRIVER={MIRAJ ODBC Driver};SERVER=127.0.0.1;PORT=7007;"
                    "UID=app;PWD=secret123;DATABASE=ventes")
cur = cn.cursor()
cur.execute("SELECT id, nom, actif FROM client WHERE ville = ?", "Alger")
print(cur.fetchall())                 # [(1, 'Amel Kaci', True)]
cur.execute("INSERT INTO client (nom, email) VALUES (?, ?) RETURNING id", "Essai", "[email protected]")
print(cur.fetchone()[0])
cn.commit()
AreaOn an instance in the PostgreSQL language
SQLthat of this manual; the ? markers are bound as parameters; ODBC escape sequences are translated ({fn UCASE('abc')}, {d '2026-10-11'}, {fn CONVERT('12', SQL_INTEGER)})
SQLGetInfoSQL_DBMS_NAME = PostgreSQL, SQL_DBMS_VER = 16.04.0000, identifier quote ", SQL_SCHEMA_TERM = schema, SQL_DATABASE_NAME = miraj
CatalogSQLTables: TABLE_CAT = miraj, TABLE_SCHEM = the schema (ventes), types TABLE, VIEW, SEQUENCE; SQLColumns: types named int4, varchar, text, numeric...; SQLPrimaryKeys (key named <table>_pkey), SQLForeignKeys, SQLStatistics
Typesbool returned as a boolean, numeric as an exact decimal; SQLGetTypeInfo: text, json, jsonb, varchar, bpchar, uuid, bool, int8, bytea, numeric, int4, int2, float4, float8, date, time, timestamp, timestamptz
Resultsseveral statements in one text: one result per statement (SQLMoreResults, cursor.nextset())
ErrorsPostgreSQL SQLSTATE (42P01, 23505...), native code 0
Unknown databaseDATABASE=inconnue: connection refused, 3D000
TLSSSLMODE=required on a server without a certificate: 08001 ("TLS exigé, mais le serveur ne le propose pas")
Generated identifierINSERT ... RETURNING (the protocol does not transmit a last identifier)

COPY is not available through ODBC. The driver's other limits are in 28.6. A third-party PostgreSQL driver (psqlODBC) also connects to such an instance.

miraj Python library#

The miraj library (26.12) offers psycopg2's DB-API interface to both languages: the same application connects to a MIRAJ or a PostgreSQL instance without changing API.

import miraj

conn = miraj.connect(host="127.0.0.1", port=7007, user="app", password="secret123",
                     dbname="ventes", protocol="postgresql")
cur = conn.cursor()
cur.execute("SELECT libelle, prix FROM article WHERE prix > %s", (2,))
  • protocol: auto (default, language recognized at connection), miraj or postgresql. dbname names the schema; an unknown schema is refused.
  • The %s become the parameters $1, $2... of a prepared statement kept per connection and reused (executemany prepares once).
  • statusmessage carries the server's tag; lastrowid is None: use INSERT ... RETURNING.
  • copy_from, copy_to, copy_expert and insert_rows go through COPY.
  • Errors: PostgreSQL SQLSTATE (e.sqlstate, e.pgcode); notices in connection.notices.
  • No named cursor (cursor(name=...): NotSupportedError): the result is read entirely at execution.

40.6 Common catalog queries#

Tables of a schema, with their size and comment:

SELECT n.nspname AS schema, c.relname AS table,
       pg_size_pretty(pg_total_relation_size(c.oid)) AS taille,
       obj_description(c.oid, 'pg_class') AS commentaire
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'miraj')
ORDER BY 1, 2;
 schema |     table      |  taille   |      commentaire
--------+----------------+-----------+------------------------
 ventes | article        | 589 bytes |
 ventes | client         | 732 bytes | Clients de la boutique
 ventes | commande       | 344 bytes |
 ventes | ligne_commande | 176 bytes |
(4 lignes)

The sizes are those of the in-memory data, indexes included, not those of the files.

Existence of a table, with no error if it is missing:

SELECT to_regclass('client') IS NOT NULL AS client, to_regclass('absente') IS NOT NULL AS absente;
 client | absente
--------+---------
 t      | f
(1 ligne)

Columns of a table through information_schema:

SELECT column_name, data_type, is_nullable, column_default, udt_name, is_identity
FROM information_schema.columns
WHERE table_schema = 'ventes' AND table_name = 'client'
ORDER BY ordinal_position;
 column_name |          data_type          | is_nullable |           column_default           | udt_name  | is_identity
-------------+-----------------------------+-------------+------------------------------------+-----------+-------------
 id          | integer                     | NO          | nextval('client_id_seq'::regclass) | int4      | NO
 nom         | character varying           | NO          |                                    | varchar   | NO
 email       | character varying           | NO          |                                    | varchar   | NO
 ville       | text                        | YES         |                                    | text      | NO
 actif       | boolean                     | NO          | true                               | bool      | NO
 cree_le     | timestamp without time zone | YES         | now()                              | timestamp | NO
(6 lignes)

Types known to the server:

SELECT typname, oid, typcategory FROM pg_type
WHERE typname IN ('bool', 'int4', 'numeric', 'text', 'timestamptz', 'jsonb', 'uuid', 'vector')
ORDER BY typname;
   typname   |  oid  | typcategory
-------------+-------+-------------
 bool        |    16 | B
 int4        |    23 | N
 jsonb       |  3802 | U
 numeric     |  1700 | N
 text        |    25 | S
 timestamptz |  1184 | D
 uuid        |  2950 | U
 vector      | 16385 | U
(8 lignes)

40.7 Sessions and parameters#

SELECT pid, usename, datname, client_addr, state, query FROM pg_stat_activity ORDER BY pid;
 pid | usename | datname | client_addr | state  |                                       query
-----+---------+---------+-------------+--------+------------------------------------------------------------------------------------
   1 | root    | miraj   | localhost   | idle   |
  28 | root    | miraj   | localhost   | active | SELECT pid, usename, datname, client_addr, state, query FROM pg_stat_activity ORDER BY pid
(2 lignes)

pg_stat_activity takes over the sessions of SHOW PROCESSLIST. An ordinary account sees only its own sessions there. application_name is the one the session gave at connection or through SET; backend_start is not filled in.

A session is closed by pg_terminate_backend(pid), or by MIRAJ's KILL statement, with its pid; pg_cancel_backend(pid) and KILL QUERY pid interrupt only the running query (57014). The closed session receives the fatal error 57P01 (terminating connection due to administrator command) before its connection ends (see chapter 37).

SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE pid <> pg_backend_pid() AND state = 'idle';
 pg_terminate_backend
----------------------
 t
 t
(2 lignes)

KILL 53 likewise closes session 53 and returns the tag KILL.

Both functions return t; for an unknown pid, f with the warning PID 99999 is not a PostgreSQL backend process (KILL answers 42704, Unknown thread id: 99999). An account closes only its own sessions, unless it has the administration privilege (PROCESS): 42501, permission denied to terminate process (You are not owner of thread 1 for KILL).

Parameters:

SELECT name, setting FROM pg_settings
WHERE name IN ('server_version', 'server_version_num', 'client_encoding', 'DateStyle', 'TimeZone',
               'max_connections', 'password_encryption', 'search_path', 'transaction_isolation',
               'statement_timeout')
ORDER BY name;
         name          |    setting
-----------------------+----------------
 DateStyle             | ISO, MDY
 TimeZone              | Etc/GMT-1
 client_encoding       | UTF8
 max_connections       | 151
 password_encryption   | scram-sha-256
 search_path           | ventes
 server_version        | 16.4
 server_version_num    | 160004
 statement_timeout     | 5min
 transaction_isolation | read committed
(10 lignes)

pg_settings brings together the parameters that PostgreSQL announces to clients and MIRAJ's session variables (autocommit, lock_wait_timeout...). As in PostgreSQL, its texts are compared and sorted byte by byte: names with capitals (DateStyle, TimeZone) come first, and name = 'datestyle' finds nothing. statement_timeout defaults to the maximum statement duration set on the server (5 minutes). current_setting('name') reads a value; settings are changed with SET (see 37).

40.8 MIRAJ tools#

MIRAJ's tools recognize the instance's language at connection time and speak that language to it. The --protocol auto | miraj | postgresql option (default auto) forces it; the connection options are the same in both languages (--host, --port, --user, --password, variable MIRAJ_PASSWORD, --ssl, --ssl-ca, --ssl-insecure, --no-ssl).

ToolOn an instance in the PostgreSQL language
miraj-cliSQL console: statements and files in the PostgreSQL language, prompt schema=>
miraj-backuphot physical backup and restore (40.9)
miraj-dumpexport to a SQL script written in the PostgreSQL language (40.9)
miraj-migratemigration to MIRAJ: definitions rewritten in the PostgreSQL language, rows loaded by COPY, routines translated to PL/pgSQL (chapter 17)
Miraj Server Managermonitoring and settings; shows the instance's language (chapter 21)
miraj-proxyrelays PostgreSQL connections unchanged (chapter 22)

miraj-cli#

$env:MIRAJ_PASSWORD = "password"
miraj-cli.exe --port 7007 -d ventes
OK, 0 ligne(s) affectée(s)
MIRAJ 1.0.0 - connecté (langage PostgreSQL) - tapez \help pour l'aide, \q pour quitter.
ventes=> SELECT code, prix FROM article ORDER BY id LIMIT 2;
+--------+------+
| code   | prix |
+--------+------+
| STY-01 | 1.30 |
| CAH-96 | 2.50 |
+--------+------+
2 ligne(s)
ventes=> SELECT 1/0;
ERREUR 22012 : division by zero
UseCommand
interactive consolemiraj-cli --port 7007 -d ventes
one or more statementsmiraj-cli --port 7007 -d ventes -e "SELECT 1 AS a; SELECT 2 AS b;"
a scriptmiraj-cli --port 7007 script.sql: statements run one by one, stopping at the first error (exit code 1)
result as JSON--json: [ {"code": "STY-01", "prix": 1.30} ], followed by 1 ligne(s)
  • With -d, the console first sets the current schema; the line OK, 0 ligne(s) affectée(s) that precedes the banner (or the first result of -e) is its report.
  • Errors are displayed with their SQLSTATE (ERREUR 42P01 : relation "absente" does not exist).
  • With -e and several statements, the results of those that precede an error are displayed, then the error; the following ones are not run.
  • A script is split according to PostgreSQL's lexical rules: standard strings ('l''été'), E'...', $$...$$.
  • \l lists the schemas (SHOW DATABASES), \d the tables of the current schema (SHOW TABLES), \d table describes a table in MIRAJ style (DESCRIBE: types int(11), longtext...). For a description in PostgreSQL types, query pg_attribute (40.2) or use SHOW CREATE TABLE.
  • Ctrl-C interrupts the running statement (a procedure loop, a long query) and returns to the prompt.
  • Exit code 2: connection impossible (wrong password: connexion à [email protected]:7007 impossible : erreur SQL 28P01 : Access denied for user 'root'@'localhost' (using password: YES)).
  • With --protocol miraj on a PostgreSQL instance, the connection fails after a wait (server disconnected): leave auto.

The rest of the console (history, \timing, source) is described in chapter 25.

40.9 Backup and restore#

Two methods, the same as in the MIRAJ language (chapter 18):

MethodNatureTool
hot physical copyschema files, immediate restoreBACKUP DATABASE / RESTORE DATABASE, miraj-backup
SQL exportscript in the PostgreSQL language, readable and portablemiraj-dump

BACKUP DATABASE and RESTORE DATABASE#

These statements are MIRAJ extensions, available in the PostgreSQL language. The server must be started with a backup folder (--backup-dir E:\sauvegardes, or <backup_dir> in miraj_config.xml); the folder named in the statement is relative to it.

BACKUP DATABASE ventes TO 'sauv1';
 Database |      Directory       | Files | Bytes | End_lsn
----------+----------------------+-------+-------+---------
 ventes   | E:\sauvegardes\sauv1 |     9 |  7247 |    1464
(1 ligne)
RESTORE DATABASE ventes_copie FROM 'sauv1';
WARNING:  base ventes restaurée sous le nom ventes_copie : les références qualifiées par l'ancien nom (vues, routines, clés étrangères) le gardent
   Database   |      Directory       | Files | Bytes | End_lsn
--------------+----------------------+-------+-------+---------
 ventes_copie | E:\sauvegardes\sauv1 |     9 |  7247 |    1464
(1 ligne)

The restored schema is usable at once (SELECT count(*) FROM ventes_copie.client;) and appears in pg_namespace. The backup does not block writes.

Without --backup-dir, the statement is refused (XX000, the server is running without the --backup-dir option so it cannot execute this statement); so is a path outside that folder (XX000, The server is running with the --backup-dir option so it cannot execute this statement).

miraj-backup#

$env:MIRAJ_PASSWORD = "password"
miraj-backup backup  --db ventes --to sauv2 --port 7007
miraj-backup restore --from sauv2 --db ventes_copie --replace --port 7007
miraj-backup verify  --from E:\sauvegardes\sauv2
miraj-backup list    --from E:\sauvegardes\sauv2
ventes : 9 fichier(s), 7.1 Ko, position 1464 → E:\sauvegardes\sauv2
ventes_copie : 9 fichier(s), 7.1 Ko, position 1464 → E:\sauvegardes\sauv2
Sauvegarde valide : base ventes, 9 fichier(s), 7.1 Ko, position 1464

backup and restore send BACKUP / RESTORE to the server (the folder is the server's, relative to its --backup-dir); --all backs up all schemas, --replace replaces an existing schema. verify and list read a local folder. The account must have the BACKUP_ADMIN privilege. A physical copy does not contain the accounts: keep them with miraj-dump --users.

miraj-dump#

miraj-dump --db ventes --users -r ventes.sql --port 7007
miraj-dump restore ventes.sql --port 7008
psql -h 127.0.0.1 -p 7008 -U root -d miraj -v ON_ERROR_STOP=1 -f ventes.sql

On an instance in the PostgreSQL language, the script is written in that language and is reloaded with miraj-dump restore, miraj-cli or psql -f:

  • tables as SHOW CREATE TABLE returns them, rows as multi-row INSERT (quoted identifiers, standard strings, booleans true / false, bytea as '\x...'::bytea);
  • sequences, views, routines and triggers in PL/pgSQL (CREATE OR REPLACE FUNCTION ... AS $function$...$function$), without DELIMITER;
  • reading takes place in a single transaction: the script reflects a single instant, without blocking the other sessions;
  • --users adds the accounts and their privileges. A reloaded account connects with a clear-text password, over TLS or on the loopback, until its password is redefined (ALTER USER ... IDENTIFIED BY ...); such a script is a sensitive file, to be protected like a password file;
  • an export follows the language of the instance it was read from: one from an instance in the MIRAJ language does not reload into an instance in the PostgreSQL language, nor the reverse.

The export options (--tables, --no-data, --no-routines...) are described in 18.3.

40.10 Not supported and differences#

TopicOn a MIRAJ instance
Missing catalog viewspg_stat_database, pg_stat_all_tables, pg_statio_user_tables, pg_stats, pg_prepared_statements, pg_cursors, pg_available_extensions, pg_timezone_names, pg_operator: 42P01
Tables present but emptypg_locks, pg_stat_user_tables, pg_depend, pg_inherits... (40.2): no locks or access statistics through them
Missing functionsinet_server_port, pg_current_wal_lsn: 42883
Size functionsin-memory sizes; pg_indexes_size is an estimate (average key width per live row)
'name'::regclass written alonereturns the OID (a number); oid::regclass returns the name
confrelid::regclass of a constraint without a target table0 (PostgreSQL: -)
Privilegesrelacl, attacl, nspacl NULL; has_table_privilege, has_column_privilege, has_any_column_privilege and has_schema_privilege return the actual privileges (8.5), the other has_*_privilege always t; pg_shadow and pg_authid reserved for whoever administers accounts, password always NULL
pg_stat_activitybackend_start not filled in; client_addr as text (localhost); an open, idle transaction stays idle
txid_current()positive number, the same throughout the transaction, but specific to MIRAJ: neither increasing from one transaction to the next, nor comparable with those of PostgreSQL
pg_settingscolumns short_desc, unit empty for MIRAJ variables; the application_name row follows the value given at connection or through SET
COMMENT ONtables and columns only; COMMENT ON VIEW, FUNCTION, SCHEMA...: 0A000
CREATE EXTENSION, VACUUMVACUUM: syntax error 42601; ANALYZE: 0A000; CREATE EXTENSION of an extension other than those in 40.2 (postgis...): 0A000. The vector extension is always present; CREATE EXTENSION vector is accepted with no effect
information_schemaMIRAJ's own: MIRAJ-specific columns (column_type, column_key, extra...); the columns added for PostgreSQL (udt_name, udt_schema, type_udt_name, table_constraints.table_catalog) also hold through a view
EXPLAINMIRAJ plan, in its own layout
Constraint namesMIRAJ's (<table>_pkey, <table>_ibfk_<n>, CONSTRAINT_<n>, column name for a column UNIQUE)
Object OIDscomputed from the name: stable, but changed by a rename
pg_get_viewdef, CHECKidentifiers always quoted; rewritten query
psql\gdesc refused
psycopg2callproc of a procedure: 42883 (write CALL); client_encoding other than UTF-8: 0A000; lastrowid = 0
ODBCno COPY
LISTEN / NOTIFYaccepted, but the client receives no asynchronous notification: see 37
DROP INDEX a, b0A000: one index per statement

The condensed list of what the catalog contains is in 29.9.