I am doing Upsert like the following in Typescript using a sequence in PostgreSQL:
let SAVE_CUSTOMER = `
INSERT INTO "db"."customers" ("id", "name")
VALUES (NEXTVAL('customers_sq'), $1)
ON CONFLICT ("id") DO UPDATE
SET "name" = $1;
`;
function saveCustomer(name: string) {
datasource.query(SAVE_CUSTOMER, name)
}
Where:
I am getting the following error message:
error: i: relation \"customers_sq\" does not exist
Things I have tried that also don't work:
NEXTVAL('db'.'customers_sq')
NEXTVAL("db"."customers_sq")
NEXTVAL("customers_sq")
I can see the sequence (also in all lower case) in my Postgres database viewer. Any tips on how to debug?
The function nextval()
expects a single argument of type regclass
, an "object identifier type", basically an alias for the base data type oid
. The manual:
All of the OID alias types for objects that are grouped by namespace accept schema-qualified names, and will display schema-qualified names on output if the object would not be found in the current search path without being qualified. For example,
myschema.mytable
is acceptable input forregclass
(if there is such a table). That value might be output asmyschema.mytable
, or justmytable
, depending on the current search path.
So if your schema name is db
and the sequence name is customers_sq
- both surprising choices! - then this is the correct call:
SELECT nextval('db.customers_sq');
Note the single argument in single-quotes.
If any "illegal" names are involved, you'll need double-quotes. See:
While the schema is listed in the search_path
of the current session, schema-qualifying the object name is optional. But schema-qualifying is never a bad idea to begin with. See:
With a proper serial
or IDENTITY
id
column, you wouldn't typically call nextval()
and insert manually. Just omit the column from the target list of INSERT
, and it will be taken care of. See:
If it's about a SQUENCE
that's owned by the column, which is the case for serial
or IDENTITY
columns, use the function pg_get_serial_sequence(table text, column text)
to get a definitive answer:
SELECT pg_get_serial_sequence('db.customers', 'id');
The result is suitably formatted for passing to the sequence functions.
To test that an object of the given name exists, without burning sequence numbers, try one of:
SELECT 'db.customers_sq'::regclass;
SELECT to_regclass('db.customers_sq');
The first raises an exception if the object does not exist, the second just returns null. To be precise, the name might resolve to any table-like object: table, view, etc. To be sure it exists and is a sequence:
SELECT *
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname = 'db' -- schema name
AND c.relname = 'customers_sq' -- sequence name
AND c.relkind = 'S'; -- is type sequence
Or, for a short, boolean answer:
SELECT EXISTS (
SELECT FROM pg_catalog.pg_class c
WHERE c.relnamespace = 'db'::regnamespace -- schema name
AND c.relname = 'customers_sq' -- sequence name
AND c.relkind = 'S' -- is type sequence
);
See: