KDB knowledge

PostgreSQL-compatible functions

Functions a PostgreSQL app expects: arrays, sequence numbers (nextval), who you are and what you may do (current_user, has_table_privilege), comments (COMMENT ON), advisory locks and session settings, so app code runs on KDB without changes.

A PostgreSQL app sends more than plain SQL. It also calls arrays, sequences and advisory locks. KDB answers all three with the PostgreSQL names, so the same app code runs here.

The other PostgreSQL function names live with their own kind: the JSON ones in JSON functions, the time ones in date and time, initcap, md5, to_str and string_to_array in text functions.

Example: A watchlist as one array value (sample data: sample)

SELECT ARRAY['CITY', 'COAST', 'VOLT'] AS watchlist,
       cardinality(ARRAY['CITY', 'COAST', 'VOLT']) AS how_many,
       array_to_string(ARRAY['CITY', 'COAST', 'VOLT'], ', ') AS shown;

Result:

watchlist (TEXT[])how_many (LONG)shown (STRING)
["CITY","COAST","VOLT"]3CITY, COAST, VOLT

(1 row)

Arrays in KDB

Syntax:

ARRAY[value, value, ...]        an array value
'["CITY","COAST"]'              JSON text an array function reads as an array
'{CITY,COAST}'                  PostgreSQL array text an array function reads too

An array is a list of values kept as one value. KDB stores it as a JSON list, so ARRAY['CITY','COAST'] shows as ["CITY","COAST"] and fits in a JSON column.

Example: A watchlist column an app reads and writes (sample data: sample)

Setup:

CREATE TABLE watchlists (user_id INT PRIMARY KEY, symbols JSON);
INSERT INTO watchlists VALUES (1, '["CITY","COAST"]'), (2, array_cat('{VOLT,ACME}', ARRAY[]));
SELECT user_id, symbols, cardinality(symbols) AS how_many,
       symbols ->> 0 AS first_symbol
FROM watchlists ORDER BY user_id;

Result:

user_id (INT)symbols (JSON)how_many (LONG)first_symbol (STRING)
1["CITY","COAST"]2CITY
2["VOLT","ACME"]2VOLT

(2 rows)

array_append

Syntax:

array_append(array, value)

The array with one more value at the end.

ArgumentTypeMeaning
arrayarray (JSON list, or array text)the list to add to
valueanythe value to put at the end

Example: Add a company to a user's watchlist (sample data: sample)

Setup:

CREATE TABLE watchlists (user_id INT PRIMARY KEY, symbols JSON);
INSERT INTO watchlists VALUES (1, '["CITY","COAST"]');
UPDATE watchlists SET symbols = array_append(symbols, 'VOLT') WHERE user_id = 1;
SELECT user_id, symbols FROM watchlists;

Result:

UPDATE watchlists SET symbols = array_append(symbols, 'VOLT') WHERE user_id = 1

1 row(s) updated

SELECT user_id, symbols FROM watchlists

user_id (INT)symbols (JSON)
1["CITY","COAST","VOLT"]

(1 row)

array_prepend

Syntax:

array_prepend(value, array)

The array with one more value at the front. The value comes first, the array second (as in PostgreSQL).

ArgumentTypeMeaning
valueanythe value to put in front
arrayarraythe list to add to

Example (sample data: sample)

SELECT array_prepend('ACME', ARRAY['CITY', 'COAST']) AS watchlist;

Result:

watchlist (JSON)
["ACME","CITY","COAST"]

(1 row)

array_cat

Syntax:

array_cat(array1, array2)

The two arrays joined into one, in order.

ArgumentTypeMeaning
array1arraythe first list
array2arraythe list added after it

Example: One watchlist for two users (sample data: sample)

SELECT array_cat(ARRAY['CITY', 'COAST'], ARRAY['VOLT']) AS together,
       array_cat(NULL, ARRAY['TELCO']) AS one_side_null,
       array_cat(NULL, NULL) AS both_null;

Result:

together (TEXT[])one_side_null (TEXT[])both_null (TEXT[])
["CITY","COAST","VOLT"]["TELCO"]NULL

(1 row)

array_remove

Syntax:

array_remove(array, value)

The array without every element equal to value.

ArgumentTypeMeaning
arrayarraythe list
valueanythe value to take out (all of them)

Example: A user removes a company from the watchlist (sample data: sample)

Setup:

CREATE TABLE watchlists (user_id INT PRIMARY KEY, symbols JSON);
INSERT INTO watchlists VALUES (1, '["CITY","COAST","CITY"]');
UPDATE watchlists SET symbols = array_remove(symbols, 'CITY') WHERE user_id = 1;
SELECT user_id, symbols FROM watchlists;

Result:

UPDATE watchlists SET symbols = array_remove(symbols, 'CITY') WHERE user_id = 1

1 row(s) updated

SELECT user_id, symbols FROM watchlists

user_id (INT)symbols (JSON)
1["COAST"]

(1 row)

array_position

Syntax:

array_position(array, value [, start])

Which place value has in the array. The first place is 1.

ArgumentTypeMeaning
arrayarraythe list to look in
valueanythe value to find
startINTlook from this place on (default 1)

Example: Where is VOLT in the watchlist? (sample data: sample)

SELECT array_position(ARRAY['CITY', 'COAST', 'VOLT'], 'VOLT') AS at_place,
       array_position(ARRAY['CITY', 'COAST', 'VOLT'], 'TELCO') AS not_there,
       array_position(ARRAY['CITY', 'COAST', 'CITY'], 'CITY', 2) AS from_place_2;

Result:

at_place (LONG)not_there (STRING)from_place_2 (LONG)
3NULL3

(1 row)

array_length, array_upper

Syntax:

array_length(array, dimension)
array_upper(array, dimension)

How many elements the array has. dimension must be 1; KDB has one dimension only. array_upper gives the number of the last place, which is the same number.

ArgumentTypeMeaning
arrayarraythe list
dimensionINTalways 1

Example (sample data: sample)

SELECT array_length(ARRAY['CITY', 'COAST'], 1) AS how_many,
       array_upper(ARRAY['CITY', 'COAST'], 1) AS last_place,
       array_length(ARRAY[], 1) AS empty_list,
       array_length(ARRAY['CITY'], 2) AS no_second_dimension;

Result:

how_many (LONG)last_place (LONG)empty_list (STRING)no_second_dimension (STRING)
22NULLNULL

(1 row)

cardinality

Syntax:

cardinality(array)

How many elements the array has, counting an empty array as 0.

ArgumentTypeMeaning
arrayarraythe list

Example: How many companies each user follows (sample data: sample)

Setup:

CREATE TABLE watchlists (user_id INT PRIMARY KEY, symbols JSON);
INSERT INTO watchlists VALUES (1, '["CITY","COAST"]'), (2, '[]'), (3, NULL);
SELECT user_id, cardinality(symbols) AS follows, array_length(symbols, 1) AS same_but_null_when_empty
FROM watchlists ORDER BY user_id;

Result:

user_id (INT)follows (LONG)same_but_null_when_empty (LONG)
122
20NULL
3NULLNULL

(3 rows)

array_to_string

Syntax:

array_to_string(array, separator [, null_text])

The elements written one after the other with separator between them.

ArgumentTypeMeaning
arrayarraythe list
separatorSTRINGtext put between two elements
null_textSTRINGwhat a NULL element is written as; without it NULL elements are left out

Example: A watchlist in one line for an email (sample data: sample)

SELECT array_to_string(ARRAY['CITY', 'COAST', 'VOLT'], ', ') AS watchlist,
       array_to_string(ARRAY['CITY', NULL, 'VOLT'], ', ') AS nulls_left_out,
       array_to_string(ARRAY['CITY', NULL, 'VOLT'], ', ', '?') AS nulls_shown;

Result:

watchlist (STRING)nulls_left_out (STRING)nulls_shown (STRING)
CITY, COAST, VOLTCITY, VOLTCITY, ?, VOLT

(1 row)

unnest

Syntax:

unnest(array)

Turns one array into many rows, one row per element, in order.

ArgumentTypeMeaning
arrayarraythe list to take apart

Example: One row per company in the list (sample data: sample)

SELECT unnest(ARRAY['CITY', 'COAST', 'VOLT']) AS symbol;

Result:

symbol (STRING)
CITY
COAST
VOLT

(3 rows)

Example: An app sends a watchlist and asks for those companies (sample data: sample)

SELECT symbol, name, sector
FROM securities
WHERE symbol IN (SELECT unnest(ARRAY['CITY', 'VOLT']))
ORDER BY symbol;

Result:

symbol (SYMBOL)name (STRING)sector (STRING)
CITYCity Bank LimitedBanking
VOLTVolt River Hydropower LimitedEnergy

(2 rows)

nextval

Syntax:

nextval('sequence_name')

The next number of a sequence. Each call gives a number that was never given before, even when many connections ask at the same time.

ArgumentTypeMeaning
sequence_nameSTRINGthe sequence name in single quotes; 'public.orders_id_seq' and '"Name"' are accepted too

Example: Order numbers from a sequence

Setup:

CREATE SEQUENCE order_no_seq START WITH 1001;
CREATE TABLE orders (order_no LONG PRIMARY KEY, symbol SYMBOL, quantity LONG);
INSERT INTO orders VALUES (nextval('order_no_seq'), 'CITY', 100),
                          (nextval('order_no_seq'), 'COAST', 50);
SELECT * FROM orders ORDER BY order_no;

Result:

INSERT INTO orders VALUES (nextval('order_no_seq'), 'CITY', 100), (nextval('order_no_seq'), 'COAST',

2 row(s) inserted

SELECT * FROM orders ORDER BY order_no

order_no (LONG)symbol (SYMBOL)quantity (LONG)
1001CITY100
1002COAST50

(2 rows)

currval

Syntax:

currval('sequence_name')

The number this connection got last from that sequence. It does not move the sequence on.

ArgumentTypeMeaning
sequence_nameSTRINGthe sequence name in single quotes

Example: The order number just given

Setup:

CREATE SEQUENCE order_no_seq START WITH 1001;
CREATE TABLE orders (order_no LONG PRIMARY KEY, symbol SYMBOL);
INSERT INTO orders VALUES (nextval('order_no_seq'), 'CITY');
SELECT currval('order_no_seq') AS my_order_no;

Result:

INSERT INTO orders VALUES (nextval('order_no_seq'), 'CITY')

1 row(s) inserted

SELECT currval('order_no_seq') AS my_order_no

my_order_no (LONG)
1001

(1 row)

Example: currval before nextval is an error (shows an error)

Setup:

CREATE SEQUENCE order_no_seq;
SELECT currval('order_no_seq');

Result:

Error: currval of sequence 'order_no_seq' is not yet defined in this session (call nextval first)

lastval

Syntax:

lastval()

The number this connection got last from any sequence.

Example: The last number of any sequence

Setup:

CREATE SEQUENCE order_no_seq START WITH 1001;
CREATE SEQUENCE invoice_no_seq START WITH 500;
SELECT nextval('order_no_seq') AS order_no, nextval('invoice_no_seq') AS invoice_no;
SELECT lastval() AS last_of_any, currval('order_no_seq') AS last_order_no;

Result:

SELECT nextval('order_no_seq') AS order_no, nextval('invoice_no_seq') AS invoice_no

order_no (LONG)invoice_no (LONG)
1001500

(1 row)

SELECT lastval() AS last_of_any, currval('order_no_seq') AS last_order_no

last_of_any (LONG)last_order_no (LONG)
5001001

(1 row)

setval

Syntax:

setval('sequence_name', value)
setval('sequence_name', value, is_called)

Sets the sequence to value. Use it after loading old rows, so new numbers start above them.

ArgumentTypeMeaning
sequence_nameSTRINGthe sequence name in single quotes
valueLONGthe number to set
is_calledBOOLTRUE (default): value counts as already given, so the next nextval is value + 1. FALSE: the next nextval gives value itself.

Example: Move the counter above the order numbers already loaded

Setup:

CREATE SEQUENCE order_no_seq START WITH 1;
CREATE TABLE orders (order_no LONG PRIMARY KEY, symbol SYMBOL);
INSERT INTO orders VALUES (5000, 'CITY'), (5001, 'COAST');
SELECT setval('order_no_seq', (SELECT max(order_no) FROM orders)) AS set_to;
INSERT INTO orders VALUES (nextval('order_no_seq'), 'VOLT');
SELECT * FROM orders ORDER BY order_no;

Result:

SELECT setval('order_no_seq', (SELECT max(order_no) FROM orders)) AS set_to

set_to (LONG)
5001

(1 row)

INSERT INTO orders VALUES (nextval('order_no_seq'), 'VOLT')

1 row(s) inserted

SELECT * FROM orders ORDER BY order_no

order_no (LONG)symbol (SYMBOL)
5000CITY
5001COAST
5002VOLT

(3 rows)

Example: is_called = FALSE: the next number is the one you gave

Setup:

CREATE SEQUENCE order_no_seq;
SELECT setval('order_no_seq', 7000, FALSE) AS set_to;
SELECT nextval('order_no_seq') AS next_number;

Result:

SELECT setval('order_no_seq', 7000, FALSE) AS set_to

set_to (LONG)
7000

(1 row)

SELECT nextval('order_no_seq') AS next_number

next_number (LONG)
7000

(1 row)

Names and sizes: to_regclass, pg_relation_size, ...

Syntax:

to_regclass('name')
pg_relation_size(relation [, 'main'])
pg_table_size(relation)
pg_indexes_size(relation)
pg_total_relation_size(relation)
pg_database_size(name_or_oid)
pg_size_pretty(bytes)
current_database()
FunctionReturns
to_regclass('name')the name of the table, index, view or sequence, or NULL when there is none
pg_relation_size, pg_table_size, pg_total_relation_sizeLONG bytes of the table on disk
pg_indexes_sizeLONG, always 0
pg_database_sizeLONG bytes of every file of the database: tables, the log, kept versions
pg_size_pretty(bytes)text like 8192 bytes, 24 kB, 1536 MB, exactly as PostgreSQL writes it
current_database()the database's name

Example: How big are the tables?

Setup:

CREATE TABLE orders (id LONG PRIMARY KEY, symbol SYMBOL, quantity LONG);
INSERT INTO orders VALUES (1, 'CITY', 100), (2, 'VOLT', 50);
SELECT to_regclass('orders') AS found, to_regclass('orderz') AS missing,
       pg_total_relation_size('orders') > 0 AS has_bytes,
       pg_size_pretty(10240) AS ten_kb, pg_size_pretty(1610612736) AS big;

Result:

found (STRING)missing (STRING)has_bytes (BOOL)ten_kb (STRING)big (STRING)
ordersNULLtrue10 kB1536 MB

(1 row)

Who and where: current_user, version()

Syntax:

current_user   current_role   user   session_user
current_schema   current_schemas(include_implicit)   current_catalog
version()
FunctionReturns
current_user, current_role, userthe user whose rights your statements use: after SET ROLE that user, else who logged in
session_userwho logged in, whatever SET ROLE did
current_schema, current_schema()public: every table is in it
current_schemas(false){public}; with true also pg_catalog, which is searched first
current_catalogthe database's name, like current_database()
version()PostgreSQL 14.0 (KDB ...), compatible server - drivers read the 14

Example: Who am I, and on what?

SELECT current_user, session_user, current_schema, current_catalog,
       version() LIKE 'PostgreSQL 14%' AS pg14;

Result:

current_user (STRING)session_user (STRING)current_schema (STRING)current_catalog (STRING)pg14 (BOOL)
kdbkdbpublicdemotrue

(1 row)

Rights: has_table_privilege and friends

Syntax:

has_table_privilege([user,] table, privilege)
has_column_privilege([user,] table, column, privilege)
has_database_privilege([user,] database, privilege)
has_schema_privilege([user,] schema, privilege)
pg_has_role([user,] role, privilege)

TRUE when the user may do it now: the same rules the statements are checked with - the user's role, changed by GRANT / REVOKE ... ON TABLE.

FunctionPrivilegesTRUE when
has_table_privilegeSELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGERSELECT needs role read, the changes write (TRUNCATE counts as DELETE), each changed by the table's grants; REFERENCES and TRIGGER need ddl
has_column_privilegeSELECT, INSERT, UPDATE, REFERENCESas for the table: KDB has no rights per column
has_database_privilegeCONNECT, TEMPORARY (TEMP), CREATEany role on that database; CREATE needs ddl
has_schema_privilegeUSAGE, CREATEUSAGE of public needs a role; pg_catalog and information_schema are open to all. CREATE in public needs ddl
pg_has_roleMEMBER, USAGEthe role is the user, or one of read, write, ddl, admin the user has here

Example: What a read-only user may do

Setup:

CREATE USER admin WITH PASSWORD 'Long-Admin-Passw0rd' ROLE admin;
CREATE USER charts WITH PASSWORD 'Chart-app-secret-11' ROLE read;
CREATE TABLE orders (id LONG PRIMARY KEY, quantity LONG);
SELECT has_table_privilege('charts', 'orders', 'SELECT') AS reads,
       has_table_privilege('charts', 'orders', 'INSERT') AS writes,
       has_schema_privilege('charts', 'public', 'CREATE') AS creates,
       pg_has_role('charts', 'read', 'MEMBER') AS is_reader;

Result:

reads (BOOL)writes (BOOL)creates (BOOL)is_reader (BOOL)
truefalsefalsetrue

(1 row)

Example: A privilege PostgreSQL does not have (shows an error)

Setup:

CREATE TABLE orders (id LONG PRIMARY KEY);
SELECT has_table_privilege('orders', 'DROP');

Result:

Error: line 1, column 8: has_table_privilege: unrecognized privilege type: "DROP"

Comments and definitions: COMMENT ON, obj_description, pg_get_indexdef

Syntax:

COMMENT ON TABLE table IS 'text' | NULL
COMMENT ON COLUMN table.column IS 'text' | NULL
obj_description(table [, 'pg_class'])       col_description(table, column_number)
pg_get_indexdef(index [, column, pretty])   pg_get_constraintdef(constraint_oid [, pretty])
pg_get_viewdef(view [, pretty])             format_type(type_oid, typmod)
pg_table_is_visible(relation)               pg_get_expr(text, relation)

COMMENT ON keeps a description with a table or a column. It stays after a restart, and \d+ in psql shows it. IS NULL (or IS '') removes it. It needs role ddl, like ALTER TABLE.

FunctionReturns
obj_description(t, 'pg_class')the table's comment, NULL when none
col_description(t, n)the comment of column n (1 = the first), NULL when none
pg_get_indexdef(i)the CREATE INDEX line of an index (also of t_pkey); with a column number, that key only
pg_get_constraintdef(oid)PRIMARY KEY (id), CHECK (...), FOREIGN KEY ... REFERENCES ..., by the oid in pg_constraint
pg_get_viewdef(v)the view's SELECT; NULL for a table
format_type(oid, typmod)the type as PostgreSQL writes it: integer, character varying(45), numeric(10,2)
pg_table_is_visible(oid)TRUE: everything is in public

Example: Describe a table and a column

Setup:

CREATE TABLE orders (id LONG PRIMARY KEY, quantity LONG);
COMMENT ON TABLE orders IS 'one row per order';
COMMENT ON COLUMN orders.quantity IS 'shares, never negative';
SELECT obj_description('orders'::regclass, 'pg_class') AS about_table,
       col_description('orders'::regclass, 2) AS about_quantity;

Result:

COMMENT ON TABLE orders IS 'one row per order'

COMMENT

COMMENT ON COLUMN orders.quantity IS 'shares, never negative'

COMMENT

SELECT obj_description(CAST('orders' AS STRING), 'pg_class') AS about_table, col_description(CAST('o

about_table (STRING)about_quantity (STRING)
one row per ordershares, never negative

(1 row)

Example: The SQL behind an index, a key and a type

Setup:

CREATE TABLE orders (id LONG PRIMARY KEY, symbol VARCHAR(12));
CREATE INDEX orders_symbol ON orders (symbol);
SELECT pg_get_indexdef('orders_symbol'::regclass) AS index_sql,
       pg_get_indexdef('orders_pkey'::regclass) AS key_sql,
       format_type(1043, 16) AS symbol_type;

Result:

index_sql (STRING)key_sql (STRING)symbol_type (STRING)
CREATE INDEX orders_symbol ON public.orders USING btree (symbol)CREATE UNIQUE INDEX orders_pkey ON public.orders USING btree (id)character varying(12)

(1 row)

Advisory locks

Syntax:

pg_advisory_lock(key)            take a lock, wait if someone else has it
pg_try_advisory_lock(key)        take it only if it is free (TRUE / FALSE)
pg_advisory_unlock(key)          give it back

An advisory lock is a name your app agrees on. It locks no table and no row - it only stops two copies of your program from doing the same job at the same time.

Say your app closes the trading day at 15:05: it reads the trade log and writes the daily candles. If two copies of the app run, the job must run once. Each copy asks for the lock 918273; the one that gets it does the job, the other one skips it.

PartMeaning
keythe lock name as a whole number (BIGINT), for example 918273
key1, key2two INT numbers instead of one; they are a different set of names (so (1, 2) and the single number 1 never meet)
_sharedmany holders at the same time (readers); a lock without _shared waits until no one holds the key
_xactthe lock ends by itself with the transaction
pg_try_*never waits: TRUE when the lock was taken, FALSE when someone else has it

Example: Only one copy of the app closes the day (sample data: sample)

SELECT pg_try_advisory_lock(918273) AS i_do_the_job;
SELECT count(*) AS trades_to_sum FROM trade_log;
SELECT pg_advisory_unlock(918273) AS given_back;

Result:

SELECT pg_try_advisory_lock(918273) AS i_do_the_job

i_do_the_job (BOOL)
true

(1 row)

SELECT count(*) AS trades_to_sum FROM trade_log

trades_to_sum (LONG)
32

(1 row)

SELECT pg_advisory_unlock(918273) AS given_back

given_back (BOOL)
true

(1 row)

pg_advisory_lock, pg_advisory_lock_shared

Syntax:

pg_advisory_lock(key)
pg_advisory_lock(key1, key2)
pg_advisory_lock_shared(key)
pg_advisory_lock_shared(key1, key2)

Takes a session lock and waits until it is free. The lock is held until pg_advisory_unlock gives it back, or the connection closes.

ArgumentTypeMeaning
keyLONGthe lock name as one whole number
key1, key2INTthe lock name as two numbers (a different set of names)

Example: Take the day-close lock, do the work, give it back (sample data: sample)

SELECT pg_advisory_lock(918273) AS taken;
SELECT symbol, sum(quantity) AS shares FROM trade_log GROUP BY symbol ORDER BY symbol;
SELECT pg_advisory_unlock(918273) AS given_back;

Result:

SELECT pg_advisory_lock(918273) AS taken

taken (STRING)

(1 row)

SELECT symbol, sum(quantity) AS shares FROM trade_log GROUP BY symbol ORDER BY symbol

symbol (SYMBOL)shares (LONG)
CITY2420
COAST3070
TELCO2770
VOLT400

(4 rows)

SELECT pg_advisory_unlock(918273) AS given_back

given_back (BOOL)
true

(1 row)

pg_advisory_xact_lock, pg_advisory_xact_lock_shared

Syntax:

pg_advisory_xact_lock(key)
pg_advisory_xact_lock(key1, key2)
pg_advisory_xact_lock_shared(key)
pg_advisory_xact_lock_shared(key1, key2)

The same, but the lock ends with the transaction. There is no unlock function for it.

ArgumentTypeMeaning
keyLONGthe lock name as one whole number
key1, key2INTthe lock name as two numbers

Example: The lock is gone after COMMIT (sample data: sample)

Setup:

BEGIN;
SELECT pg_advisory_xact_lock(918273) AS taken_for_this_transaction;
COMMIT;
SELECT pg_try_advisory_lock(918273) AS free_again;

Result:

free_again (BOOL)
true

(1 row)

pg_try_advisory_lock, pg_try_advisory_lock_shared

Syntax:

pg_try_advisory_lock(key)
pg_try_advisory_lock(key1, key2)
pg_try_advisory_lock_shared(key)
pg_try_advisory_lock_shared(key1, key2)

Takes a session lock only if it is free right now. It never waits.

ArgumentTypeMeaning
keyLONGthe lock name as one whole number
key1, key2INTthe lock name as two numbers

Example: Skip the job when another copy already has the lock (sample data: sample)

SELECT pg_try_advisory_lock(918273) AS first_call,
       pg_try_advisory_lock(918273) AS same_connection_again;
SELECT pg_advisory_unlock(918273) AS unlock_1, pg_advisory_unlock(918273) AS unlock_2;

Result:

SELECT pg_try_advisory_lock(918273) AS first_call, pg_try_advisory_lock(918273) AS same_connection_a

first_call (BOOL)same_connection_again (BOOL)
truetrue

(1 row)

SELECT pg_advisory_unlock(918273) AS unlock_1, pg_advisory_unlock(918273) AS unlock_2

unlock_1 (BOOL)unlock_2 (BOOL)
truetrue

(1 row)

pg_try_advisory_xact_lock, pg_try_advisory_xact_lock_shared

Syntax:

pg_try_advisory_xact_lock(key)
pg_try_advisory_xact_lock(key1, key2)
pg_try_advisory_xact_lock_shared(key)
pg_try_advisory_xact_lock_shared(key1, key2)

Takes a transaction lock only if it is free right now. It never waits, and it ends with the transaction.

ArgumentTypeMeaning
keyLONGthe lock name as one whole number
key1, key2INTthe lock name as two numbers

Example: Take it only if free, inside a transaction (sample data: sample)

BEGIN;
SELECT pg_try_advisory_xact_lock(918273) AS got_it;
COMMIT;

Result:

BEGIN

transaction started

SELECT pg_try_advisory_xact_lock(918273) AS got_it

got_it (BOOL)
true

(1 row)

COMMIT

transaction committed: 0 change(s) saved

pg_advisory_unlock, pg_advisory_unlock_shared

Syntax:

pg_advisory_unlock(key)
pg_advisory_unlock(key1, key2)
pg_advisory_unlock_shared(key)
pg_advisory_unlock_shared(key1, key2)

Gives back one session lock this connection holds.

ArgumentTypeMeaning
keyLONGthe lock name as one whole number
key1, key2INTthe lock name as two numbers

Example: Unlocking a key you do not hold gives FALSE (sample data: sample)

SELECT pg_try_advisory_lock(918273) AS taken;
SELECT pg_advisory_unlock(918273) AS given_back,
       pg_advisory_unlock(918273) AS again,
       pg_advisory_unlock(55) AS never_held;

Result:

SELECT pg_try_advisory_lock(918273) AS taken

taken (BOOL)
true

(1 row)

SELECT pg_advisory_unlock(918273) AS given_back, pg_advisory_unlock(918273) AS again, pg_advisory_un

given_back (BOOL)again (BOOL)never_held (BOOL)
truefalsefalse

(1 row)

pg_advisory_unlock_all

Syntax:

pg_advisory_unlock_all()

Gives back every session lock this connection holds.

Example: Clean up after a job (sample data: sample)

SELECT pg_advisory_lock(918273) AS lock_1;
SELECT pg_advisory_lock(918274) AS lock_2;
SELECT pg_advisory_unlock_all() AS all_given_back;
SELECT pg_try_advisory_lock(918273) AS free_again;

Result:

SELECT pg_advisory_lock(918273) AS lock_1

lock_1 (STRING)

(1 row)

SELECT pg_advisory_lock(918274) AS lock_2

lock_2 (STRING)

(1 row)

SELECT pg_advisory_unlock_all() AS all_given_back

all_given_back (STRING)

(1 row)

SELECT pg_try_advisory_lock(918273) AS free_again

free_again (BOOL)
true

(1 row)

Session settings: current_setting, set_config

Syntax:

current_setting(name)               the value, an error when it was never set
current_setting(name, missing_ok)   the value, or NULL when missing_ok is TRUE
set_config(name, value, is_local)   sets it and returns the value
SET name = 'value'                  the same as set_config(name, value, FALSE) (Postgres port)
SET LOCAL name = 'value'            the same as set_config(name, value, TRUE) (Postgres port)

A setting is a named text value your connection keeps. Apps use names of their own with a dot in them, like app.allow_edit, to tell a trigger what the next statement may do.

PartMeaning
namethe setting's name, any case; your own names have a dot: nchartpro.allow_identity_update
valuetext
is_localTRUE: only until the transaction ends (COMMIT or ROLLBACK); FALSE: until the connection closes
missing_okTRUE: a name never set gives NULL instead of an error

Example: Set a value, read it back, and ask for one never set (sample data: sample)

SELECT set_config('app.allow_edit', 'on', TRUE) AS set_to,
       current_setting('app.allow_edit') AS read_back,
       current_setting('app.never_set', TRUE) AS missing,
       current_setting('TimeZone') AS time_zone;

Result:

set_to (STRING)read_back (STRING)missing (STRING)time_zone (STRING)
ononNULLUTC

(1 row)

A trigger that lets a DBA through only when the statement asked for it:

CREATE FUNCTION guard_email() RETURNS trigger AS $$
BEGIN
  IF current_setting('app.allow_edit', true) = 'on' THEN
    RETURN NEW;
  END IF;
  IF NEW.email IS DISTINCT FROM OLD.email THEN
    RAISE EXCEPTION 'email is write-once';
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- the DBA, on the Postgres port:
BEGIN;
SET LOCAL app.allow_edit = 'on';
UPDATE users SET email = 'new@x.com' WHERE id = 7;
COMMIT;   -- the setting ends here