How do i get nextval without advancing the cursor? - postgresql

How do i get nextval without advancing the cursor in PostgreSQL?
This doesn't work:
pgdb=# SELECT curval('schemas.category_category_id_seq');
ERROR: function curval(unknown) does not exist
LINE 1: SELECT curval('schemas.category_category_id_seq');
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.

PostgreSQL's nextval() function is safe. But currval()+1 isn't.

From what I can tell, your function name is just wrong. It is "currval". See https://dba.stackexchange.com/questions/3281/how-do-i-use-currval-in-postgresql-to-get-the-last-inserted-id
BTW, if you can assume your sequence just increments by one, you can do:
select currval('schemas.category_category_id_seq') + 1;

Related

Is it possible to use uppercase strings in pgTap without it assuming it's a prepared statement?

We're attempting to run tests against a postgres function that returns a SETOF VARCHAR uppercase strings.
Whenever this test case runs, however, pgTap tries to look up a prepared statement with the same name as the uppercase value we're expecting to be returned. Is there any way of escaping this behaviour or calling this test case in another way to check output?
SELECT set_eq(
the_function_call(_property := 'value'),
$$ VALUES ('FIRST_RETURN'), ('SECOND_RETURN') $$,
'returns both expected values'
);
psql:/path/to/test.test.sql:20: ERROR: prepared statement "second_return" does not exist
CONTEXT: SQL statement "CREATE TEMP TABLE __taphave__ AS EXECUTE SECOND_RETURN"
I've tried backslashes, quotes, using UPPER on lowercase strings, casting the values as VARCHAR and ARRAY ['FIRST_RETURN', 'SECOND_RETURN'] as the second argument but still get the same prepared statement issue.
I was trying to avoid creating a prepared statement for each value that literally returns the same value again, but if that's the only way I guess I'll have to relent! Any help greatly appreciated.
Managed to get this working by changing the way the test is called;
SELECT set_eq(
$$ SELECT * FROM the_function_call(_property := 'value') $$,
ARRAY ['FIRST_RETURN', 'SECOND_RETURN'],
'returns both expected values'
);
Now passes with no issues

pg_get_serial_sequence in postgres fails and returns misleading error

This is not obviuos to me.
When I do:
SELECT MAX("SequenceNumber") FROM "TrackingEvent";
It returns perfectly fine with the correct result
When I do:
SELECT nextval(pg_get_serial_sequence("TrackingEvent", "SequenceNumber")) AS NextId
It returns an error which says
column "TrackingEvent" does not exist.
Not only is it wrong but the first argument of the function pg_get_serial_sequence takes a table name and not a column name, so the error is aslo misleading.
Anyways, can someone explain to me why I get an error on the pg_get_serial_sequence function ?
pg_get_serial_sequence() expects a string as its argument, not an identifier. String constants are written with single quotes in SQL, "TrackingEvent" is an identifier, 'TrackingEvent' is a string constant.
But because the function converts the string constant to an identifier, you need to include the double quotes as part of the string constant. This however only applies to the table name, not the column name, as explained in the manual
Because the first parameter is potentially a schema and table, it is not treated as a double-quoted identifier, meaning it is lower cased by default, while the second parameter, being just a column name, is treated as double-quoted and has its case preserved.
So you need to use:
SELECT nextval(pg_get_serial_sequence('"TrackingEvent"', 'SequenceNumber'))
This is another good example why using quoted identifiers is a bad idea. You should rename "TrackingEvent" to tracking_event and "SequenceNumber" to sequence_number

postgres function takes parameter bytea while calling insted of varchar

ERROR: function dharani.fn_generate_ror_1b_citizen(bytea, character varying) does not exist at character 15
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
STATEMENT: select * from dharani.fn_generate_ror_1b_citizen($1,$2)
ERROR: function dharani.fn_generate_pahani_citizen(bytea, bytea, character varying) does not exist at character 15
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
STATEMENT: select * from dharani.fn_generate_pahani_citizen($1,$2,$3)
You have to supply function arguments of the appropriate type, if necessary using type casts as the hint says.
SQL is a typed language, and PostgreSQL uses function overloading, so it is essential the data types are resolved properly. The exact rules how this is done are in the documentation.

Inconsistent PostgreSQL behavior with strings and network functions

PostgreSQL's inet functions generally accept strings in correct inet form:
mydb=# select network('10.1.2.3/24');
network
-------------
10.1.2.0/24
(1 row)
However, if string concatenation is used to construct the string, the same function will fail:
mydb=# select network('10.1.2.3' || '/24');
ERROR: function network(text) does not exist
LINE 1: select network('10.1.2.3' || '/24');
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
even though the two strings appear to be equivalent:
mydb=# select '10.1.2.3/24';
?column?
-------------
10.1.2.3/24
(1 row)
mydb=# select '10.1.2.3' || '/24';
?column?
-------------
10.1.2.3/24
(1 row)
You can work around this by casting the concatenated string to inet:
mydb=# select network(('10.1.2.3' || '/24')::inet);
network
-------------
10.1.2.0/24
(1 row)
Can anyone describe this behavior in a way that will help me understand why it happens? Is there implicit casting of whole strings that doesn't affect concatenated strings?
Strictly speaking, a quoted literal in PostgreSQL (e.g. '10.1.2.3/24') is not a string, it is a literal of some type to be determined by the parser.
You can tell the parser what type it is using SQL-standard prefix notation (inet '10.1.2.3/24') or Postgres suffix-cast notation ('10.1.2.3/24'::inet). These are not type-casts, they are the input specification for the particular type. If you don't specify the type, the parser will try to guess at its type based on context, and fall back to the pseudo-type unknown.
When you run network('10.1.2.3/24'), the literal is deduced to be of type inet, because there is only one function called network, and that is the type it expects. The value is never of type text, so no cast is needed.
But when you write network('10.1.2.3' || '/24'), the || operator needs to be resolved first; to do this, Postgres determines (correctly) that the literals '10.1.2.3' and '/24' should be treated as type text, and the result of concatenating them is also text. It then looks for a function called network() which takes a text parameter and doesn't find one; it has no automatic cast configured text to inet, so it fails with a type-checking error.
You can actually see this in action by creating a new function called network that takes a float parameter:
create function network(float) returns text language sql as $$ select 'test'::text; $$
Running select network('10.1.2.3/24') with this function in scope will give you an error "Could not choose a best candidate function", because Postgres doesn't know whether inet or float is the type you intended with your literal '10.1.2.3/24'. (If you create a function network(text), it will be preferred over both; not because the literal is of type text, just because it is considered a "better candidate".)
Conversely, if you specify Select network(text '10.1.2.3/24'), you will get a type error, not an implicit cast.
There seems to be an implicit type cast to inet before applying the network function. This doesn't work when you are explicitly creating a text object by concatenating. You should add an explicit inet type cast:
db=# select network(('10.1.2.3'||'/24')::inet);
network
-------------
10.1.2.0/24
(1 row)

PostgreSQL Trim excessive trailing zeroes: type numeric but expression is of type text

I'm trying to clean out excessive trailing zeros, I used the following query...
UPDATE _table_ SET _column_=trim(trailing '00' FROM '_column_');
...and I received the following error:
ERROR: column "_column_" is of
expression is of type text.
I've played around with the quotes since that usually is what it barrels down to for text versus numeric though without any luck.
The CREATE TABLE syntax:
CREATE TABLE _table_ (
id bigint NOT NULL,
x bigint,
y bigint,
_column_ numeric
);
You can cast the arguments from and the result back to numeric:
UPDATE _table_ SET _column_=trim(trailing '00' FROM _column_::text)::numeric;
Also note that you don't quote column names with single quotes as you did.
Postgres version 13 now comes with the trim_scale() function:
UPDATE _table_ SET _column_ = trim_scale(_column_);
trim takes string parameters, so _column_ has to be cast to a string (varchar for example). Then, the result of trim has to be cast back to numeric.
UPDATE _table_ SET _column_=trim(trailing '00' FROM _column_::varchar)::numeric;
Another (arguably more consistent) way to clean out the trailing zeroes from a NUMERIC field would be to use something like the following:
UPDATE _table_ SET _column_ = CAST(to_char(_column_, 'FM999999999990.999999') AS NUMERIC);
Note that you would have to modify the FM pattern to match the maximum expected precision and scale of your _column_ field. For more details on the FM pattern modifier and/or the to_char(..) function see the PostgreSQL docs here and here.
Edit: Also, see the following post on the gnumed-devel mailing list for a longer and more thorough explanation on this approach.
Be careful with all the answers here. Although this looks like a simple problem, it's not.
If you have pg 13 or higher, you should use trim_scale (there is an answer about that already). If not, here is my "Polyfill":
DO $x$
BEGIN
IF count(*)=0 FROM pg_proc where proname='trim_scale' THEN
CREATE FUNCTION trim_scale(numeric) RETURNS numeric AS $$
SELECT CASE WHEN trim($1::text, '0')::numeric = $1 THEN trim($1::text, '0')::numeric ELSE $1 END $$
LANGUAGE SQL;
END IF;
END;
$x$;
And here is a query for testing the answers:
WITH test as (SELECT unnest(string_to_array('1|2.0|0030.00|4.123456000|300000','|'))::numeric _column_)
SELECT _column_ original,
trim(trailing '00' FROM _column_::text)::numeric accepted_answer,
CAST(to_char(_column_, 'FM999999999990.999') AS NUMERIC) another_fancy_one,
CASE WHEN trim(_column_::text, '0')::numeric = _column_ THEN trim(_column_::text, '0')::numeric ELSE _column_ END my FROM test;
Well... it looks like, I'm trying to show the flaws of the earlier answers, while just can't come up with other testcases. Maybe you should write more, if you can.
I'm like short syntax instead of fancy sql keywords, so I always go with :: over CAST and function call with comma separated args over constructs like trim(trailing '00' FROM _column_). But it's a personal taste only, you should check your company or team standards (and fight for change them XD)