I have a question. I'm using a Postgrs database, and my problem is that I need to use the ints as text. I have the following solution:
CREATE FUNCTION pg_catalog.text(integer) RETURNS text STRICT IMMUTABLE LANGUAGE SQL AS 'SELECT textin(int4out($1));';
CREATE CAST (integer AS text) WITH FUNCTION pg_catalog.text(integer) AS IMPLICIT;
COMMENT ON FUNCTION pg_catalog.text(integer) IS 'convert integer to text';
I had been reading that this solution is not correct, it can cause some problems in the future. So I had been doing a research through the internet and I saw some people only use CAST, but just to convert a specific int, i.e., https://dba.stackexchange.com/questions/82511/how-to-enable-implicit-casts-in-postgresql-9-2
The danger with creating an implicit cast like that is that it destabilizes the carefully balanced type system in PostgreSQL; after that, some innocent invocations of overloaded functions will stop working because due to the cast, there are suddenly too many candidate functions to make a unique choice.
It is much better to use an explicit cast:
CAST (intcol AS text)
That is standard SQL and should work everywhere.
Related
It's definitely possible to define such a function through the C API as can be witnessed by looking at json_build_array() for example:
Unfortunately trying to create a SQL or pl/pgSQL function like this fails
CREATE OR REPLACE FUNCTION my_function( VARIADIC _args anyelement[]) RETURNS ...
and this is no surprise as the PostgreSQL documentation mentions that 'VARIADIC' only works for arrays (and it seems there is no such thing as an anyelement[]). Does anybody know how to get this working without having to write a C function ?
There is not possibility now. PLpgSQL and SQL languages are out of game, because there are not arrays with fields of different types. And PL/Perl or PL/Python just doesn't support, it what I know.
I have a user defined type:
create type match_input as (
_values text[],
_name text,
_norm_fn text,
_operator text
);
and I use this as the input parameter for a function:
get_matches(mi match_input)
I want to be able to call the same function, but passing a single text value for _values. So I defined a new type:
create type match_input_simple as (
_values text,
_name text,
_norm_fn text,
_operator text
);
If I try and overload the function with the following:
create or replace function get_matches(_mis match_input_simple)
returns setof contact_index
as $func$
select get_matches((array[_mis._values], _mis._name,
_mis._norm_fn, _mis._operator)::match_input);
$func$
language sql strict;
The function compiles without complaining, but when I run the function I get this error:
ERROR: function get_matches(record) is not unique
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
So it looks like postgres can't tell the difference between my two different record types when its trying to decide which function to run.
I don't want the user to have to explicitly type cast the record each time the function is called, since that kind of defeats the purpose of trying to make the interface simple.
It is getting confused over type on the similar functions. Explicit type cast might help. Like
SELECT get_matches(val::match_input_simple)
Or
SELECT get_matches(val::match_input )
Depending upon the type of val
It can't be done without casting.
Remember that you can have multiple types with the exactly same content and create a overload for each one of them. How would the database choose the right type and function?
I'm trying to write my first PL/pgSQL function. For right now it is simply supposed to return the number of characters in a value that is passed to it.
CREATE OR REPLACE FUNCTION public.cents(money)
RETURNS int
LANGUAGE plpgsql
LEAKPROOF
AS $function$
DECLARE
new_price money;
size int;
BEGIN
size := char_length(money);
RETURN size;
END;
$function$;
When I try to test with $66.66 I get one error:
select cents($66.66);
ERROR: syntax error at or near ".66"
LINE 1: select cents($66.66);
^
And if I use $66 I get a different error:
select cents($66);
ERROR: there is no parameter $66
LINE 1: select cents($66);
^
Using just the integer 66 gives me a third error:
select cents(66);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
What am I doing wrong here?
Are you sure you want to use the data type money? Consider:
PostgreSQL: Which Datatype should be used for Currency?
If you need the type money, be sure to understand the role of locale settings for this type. Read the manual.
You can't enter literals without single quotes (like numeric constants) - unless you cast them, which would be inefficient. But that's not the only error. Your function would work like this:
CREATE OR REPLACE FUNCTION public.cents(_my_money_parameter money)
RETURNS int AS
$func$
BEGIN
RETURN char_length(_my_money_parameter::text);
END
$func$ LANGUAGE plpgsql LEAKPROOF;
Call:
SELECT public.cents(money '66.66');
SELECT public.cents('66.66'::money);
SELECT public.cents(66.66::money);
The 1st call variant is the most efficient, but it depends on locale settings. The dot in my example is interpreted as thousands separator and ignored (not as decimal point) in some locales.
Notes
You treat money like a parameter name in the function body, but it's just the data type. If you want to use parameter names, you have to declare them like demonstrated. Or refer to parameters with positional references: $1, $2 etc.
char_length() expects a character data type, you cannot use it for data type money without casting. Just length() is equivalent.
If you include the dollar sign, you need single quotes for the string literal: '$66.66' - and the format must match your locale setting to work for money.
If you just supply the numeric constant 66, Postgres won't find the function with a money parameter due to the rules of function type resolution. Details:
Is there a way to disable function overloading in Postgres
Start by reading the chapter Constants in the manual.
Continue with the page on CREATE FUNCTION.
You need to put single-quotes around your input:
SELECT cents('$66.66');
If your settings don't allow this (still throw an error) you can try casting:
SELECT cents('66.66'::float8::numeric::money);
Be sure to reference the docs as they provide a good overview:
https://www.postgresql.org/docs/current/static/datatype-money.html
I'm trying to create a type to store color hexes preferably in byte form. I followed all the instructions in postgres docs here: http://www.postgresql.org/docs/9.3/static/sql-createtype.html
and found the part where it says CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ; a bit... unsettling. What goes in the ellipses? Turns out that was justified as I cannot find any example of creating a simple data type without custom PG functions written in C.
My best attempt was this:
CREATE TYPE color;
CREATE FUNCTION color_in(cstring) RETURNS color AS $$
BEGIN
RETURN decode($1::text, 'hex')::color;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE RETURNS NULL ON NULL INPUT;
CREATE FUNCTION color_out(color) RETURNS cstring AS $$
BEGIN
RETURN encode($1::bytea, 'hex')::text;
END;
$$ LANGUAGE PLPGSQL IMMUTABLE RETURNS NULL ON NULL INPUT;
CREATE TYPE color (
INTERNALLENGTH = 3,
LIKE = bytea,
INPUT = color_in,
OUTPUT = color_out
);
This yields the error:
NOTICE: return type color is only a shell
ERROR: PL/pgSQL functions cannot return type color
Similar error if I use Language SQL or default to SPL.
The example in-out functions are listed here: http://www.postgresql.org/docs/9.3/static/xtypes.html. The only example functions are written in C. Am I correct in assuming this is the only way to write a UDT in postgres? Is there another way? My goal is to have the colors stored as bytes but have their native text form be hexadecimal (for the purposes of dumping, restoring, and casting from raw).
From http://www.postgresql.org/docs/9.4/interactive/datatype-pseudo.html
Functions coded in procedural languages can use pseudo-types only as
allowed by their implementation languages. At present the procedural
languages all forbid use of a pseudo-type as argument type, and allow
only void and record as a result type (plus trigger when the function
is used as a trigger). Some also support polymorphic functions using
the types anyelement, anyarray, anynonarray, anyenum, and anyrange.
And from the docs for create type
http://www.postgresql.org/docs/9.4/interactive/sql-createtype.html
The input function can be declared as taking one argument of type
cstring, or as taking three arguments of types cstring, oid, integer.
The output function must return type cstring.
Given the above:
Base data type input and output functions use cstring.
Procedural languages can not use cstring.
Base data type functions can't be written in procedural language.
Defining a base data type can't be done purely in procedural languages.
You can use pre-existing input and output functions, but I don't think
that any of them will directly get you a hex string. The closest
is probably the \x hex escaped bytea, but you'd have a \x at the beginning of your text representation. If you're willing to cast to and from text, I think you could create a type using bytea_in and bytea_out and writing custom casts to and from text. You'd have to explicitly cast to avoid the \x though.
My users and I do not use function overloading in PL/pgSQL. We always have one function per (schema, name) tuple. As such, we'd like to drop a function by name only, change its signature without having to drop it first, etc. Consider for example, the following function:
CREATE OR REPLACE FUNCTION myfunc(day_number SMALLINT)
RETURNS TABLE(a INT)
AS
$BODY$
BEGIN
RETURN QUERY (SELECT 1 AS a);
END;
$BODY$
LANGUAGE plpgsql;
To save time, we would like to invoke it as follows, without qualifying 1 with ::SMALLINT, because there is only one function named myfunc, and it has exactly one parameter named day_number:
SELECT * FROM myfunc(day_number := 1)
There is no ambiguity, and the value 1 is consistent with SMALLINT type, yet PostgreSQL complains:
SELECT * FROM myfunc(day_number := 1);
ERROR: function myfunc(day_number := integer) does not exist
LINE 12: SELECT * FROM myfunc(day_number := 1);
^
HINT: No function matches the given name and argument types.
You might need to add explicit type casts.
When we invoke such functions from Python, we use a wrapper that looks up functions' signatures and qualifies parameters with types. This approach works, but there seems to be a potential for improvement.
Is there a way to turn off function overloading altogether?
Erwin sent a correct reply. My next reply is related to possibility to disable overloading.
It is not possible to disable overloading - this is a base feature of PostgreSQL function API system - and cannot be disabled. We know so there are some side effects like strong function signature rigidity - but it is protection against some unpleasant side effects when function is used in Views, table definitions, .. So you cannot to disable it.
You can simply check if you have or have not overloaded functions:
postgres=# select count(*), proname
from pg_proc
where pronamespace <> 11
group by proname
having count(*) > 1;
count | proname
-------+---------
(0 rows)
This is actually not directly a matter of function overloading (which would be impossible to "turn off"). It's a matter of function type resolution. (Of course, that algorithm could be more permissive without overloaded functions.)
All of these would just work:
SELECT * FROM myfunc(day_number := '1');
SELECT * FROM myfunc('1'); -- note the quotes
SELECT * FROM myfunc(1::smallint);
SELECT * FROM myfunc('1'::smallint);
Why?
The last two are rather obvious, you mentioned that in your question already.
The first two are more interesting, the explanation is buried in the Function Type Resolution:
unknown literals are assumed to be convertible to anything for this purpose.
And that should be the simple solution for you: use string literals.
An untyped literal '1' (with quotes) or "string literal" as defined in the SQL standard is different in nature from a typed literal (or constant).
A numeric constant 1 (without quotes) is cast to a numeric type immediately. The manual:
A numeric constant that contains neither a decimal point nor an
exponent is initially presumed to be type integer if its value fits in
type integer (32 bits); otherwise it is presumed to be type bigint if
its value fits in type bigint (64 bits); otherwise it is taken to be
type numeric. Constants that contain decimal points and/or exponents
are always initially presumed to be type numeric.
The initially assigned data type of a numeric constant is just a
starting point for the type resolution algorithms. In most cases the
constant will be automatically coerced to the most appropriate type
depending on context. When necessary, you can force a numeric value to
be interpreted as a specific data type by casting it.
Bold emphasis mine.
The assignment in the function call (day_number := 1) is a special case, the data type of day_number is unknown at this point. Postgres cannot derive a data type from this assignment and defaults to integer.
Consequently, Postgres looks for a function taking an integer first. Then for functions taking a type only an implicit cast away from integer, in other words:
SELECT casttarget::regtype
FROM pg_cast
WHERE castsource = 'int'::regtype
AND castcontext = 'i';
All of these would be found - and conflict if there were more than one function. That would be function overloading, and you would get a different error message. With two candidate functions like this:
SELECT * FROM myfunc(1);
ERROR: function myfunc(integer) is not unique
Note the "integer" in the message: the numeric constant has been cast to integer.
However, the cast from integer to smallint is "only" an assignment cast. And that's where the journey ends:
No function matches the given name and argument types.
SQL Fiddle.
More detailed explanation in these related answers:
PostgreSQL ERROR: function to_tsvector(character varying, unknown) does not exist
Generate series of dates - using date type as input
Dirty fix
You could fix this by "upgrading" the cast from integer to smallint to an implicit cast:
UPDATE pg_cast
SET castcontext = 'i'
WHERE castsource = 'int'::regtype
AND casttarget = 'int2'::regtype;
But I would strongly discourage tampering with the default casting system. Only consider this if you know exactly what you are doing. You'll find related discussions in the Postgres lists. It can have all kinds of side effects, starting with function type resolution, but not ending there.
Aside
Function type resolution is completely independent from the used language. An SQL function would compete with PL/perl or PL/pgSQL or "internal" functions just the same. The function signature is essential. Built-in functions only come first, because pg_catalog comes first in the default search_path.
There are plenty of in built functions that are overloaded, so it simply would not work if you turned off function overloading.