AWS Redshift table-valued function - amazon-redshift

I'm testing AWS Redshift as an option for building Data Warehouse.
According to document, http://docs.aws.amazon.com/redshift/latest/dg/user-defined-functions.html
I've found that I can create scalar user-defined function with python.
But I'm wondering that if Redshift supports table-valued function? I have done some research but haven't found any information about that.
Any ideas? Thanks a lot

Return a Scalar Array! then split part you way to table land.
CREATE OR REPLACE FUNCTION udfArray ()
RETURNS text IMMUTABLE AS
$$
return 'Matthew,5,44'
$$ LANGUAGE plpythonu;
SELECT
Split_Part(t1.scalarOut, ',',1) val1,
Split_Part(t1.scalarOut, ',',2) val2,
Split_Part(t1.scalarOut, ',',3) val3
FROM
(SELECT udfArray () scalarOut) t1;

Related

postgres function to remove numbers from data in columns

I've found a stackoverflow answer on how to create a function in MSSQL to remove numerical values from a varchar column, I need to translate this into Postgres if possible. This could do with being a function as I need to use it multiple times across a few different databases.
I've tried my best to convert the MSSQL version but it's still not working, could anyone help fill in the gaps? I'm not a database expert at all, thanks!
CREATE Function public.RemoveNumericCharacters(Temp VarChar(1000))
Returns VarChar(1000)
AS $$
Declare NumRange varchar(50) = '%[0-9]%';
While PatIndex(#NumRange, Temp) > 0
Set Temp = Stuff(Temp, PatIndex(#NumRange, Temp), 1, '')
Return Temp
End
$$
LANGUAGE SQL;
For a bit more context - the column I need this for is an email column so it will need to convert for example testuser345#hotmail.com to testuser#hotmail.com.
No need for your own function, this can be achieved using regexp_replace()
select regexp_replace('testuser345#hotmail.com', '[0-9]+', 'g')
Obviously you can put this into a function:
CREATE Function public.removenumericcharacters(p_input text)
returns text
AS $$
select regexp_replace(p_input, '[0-9]+', 'g');
$$
LANGUAGE SQL
immutable;

Workaround for inlined SQL function dispatch in PostgreSQL

Goal
I'm trying to make PostgreSQL do something like function dispatch, but I'm open to any other solution for the problem that lets me keep the function I'm calling a SQL function (versus PL/PGSQL) because I want it to be inlined.
Suppose I have two functions like:
create or replace function is_in_view_one(p people) returns boolean as $$
select p.state = ANY(ARRAY['TX', 'NY', 'CA'])
$$ language sql strict stable;
And:
create or replace function is_in_view_two(p people) returns boolean as $$
select p.state = ANY(ARRAY['NV', 'FL', 'MT'])
$$ language sql strict stable;
I want to be able to write some code, or adapt the above functions, so I can write:
select count(*)
from people
where is_in_view(people, 'one');
And I want is_in_view to be fully inline-able according to these criteria: https://wiki.postgresql.org/wiki/Inlining_of_SQL_functions'
Attempted Solution via Domains
I've tried to set up a solution using domains as function identifiers, and although it doesn't work, I think someone more knowledgeable about PostgreSQL types, casts, and function identification might know how to hack it.
I tried to do:
create domain view_one_id as uuid check (value = 'ed744964-6561-11eb-878e-c7ad77d3260a');
create domain view_two_id as uuid check (value = 'fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
create or replace function say_n(v view_one_id) returns integer as $$
select 1
$$ language sql strict;
create or replace function say_n(v view_two_id) returns integer as $$
select 2
$$ language sql strict;
Hoping that I could then do:
select say_n('ed744964-6561-11eb-878e-c7ad77d3260a') # 1
select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c') # 2
But instead I get:
=# select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
ERROR: function say_n(unknown) is not unique
LINE 1: select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c');
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
I can do:
=# select say_n('fa9fe0f8-6561-11eb-878e-c79c81b46d0c'::view_two_id);
say_n
-------
2
(1 row)
But in order to integrate with external tooling that knows how to call functions and only call functions (not also supply a variable type cast) I'm holding out hope for a solution that doesn't require modifications to this external tooling.
Happy to entertain alternatives! I feel like this solution might be possible by fiddling with the CAST or something, however.
What about using CASE:
select p.state = ANY (CASE WHEN $2 = 'one'
THEN ARRAY['TX', 'NY', 'CA']
ELSE ARRAY['NV', 'FL', 'MT']
END)
But if you want efficiency, you would be better off with the two functions, because the above cannot use an index.

How to return a table by rowtype in PL/pgSQL

I am trying to implement a function that returns a table with the same structure as an input table in the parameter, using PL/pgSQL (PostgreSQL 9.3). Basically, I want to update a table, and return a copy of the updated table with plpgsql. I searched around SO and found several related questions (e.g. Return dynamic table with unknown columns from PL/pgSQL function and Table name as a PostgreSQL function parameter), which lead to the following minimal test example:
CREATE OR REPLACE FUNCTION change_val(_lookup_tbl regclass)
RETURNS _lookup_tbl%rowtype AS --problem line
$func$
BEGIN
RETURN QUERY EXECUTE format('UPDATE %s SET val = 2 RETURNING * ; ', _lookup_tbl);
END
$func$ LANGUAGE plpgsql;
But I can't get past giving the correct return type for TABLE or SETOF RECORD in the problem line. According to this answer:
SQL demands to know the return type at call time
But I think the return type (which I intend to borrow from the input table type) is known. Can some one help explain if it is possible to fix the signature of the above PL/pgSQL function?
Note, I need to parametrize the input table and return the update of that table. Alternatives are welcome.
What you have so far looks good. The missing ingredient: polymorphic types.
CREATE OR REPLACE FUNCTION change_val(_tbl_type anyelement)
RETURNS SETOF anyelement -- problem solved
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
'UPDATE %s SET val = 2 RETURNING *;'
, pg_typeof(_tbl_type))
);
END
$func$;
Call (important):
SELECT * FROM change_val(NULL::some_tbl);
db<>fiddle here
Old sqlfiddle
See (last paragraph):
Refactor a PL/pgSQL function to return the output of various SELECT queries

Retrieving a value from a RECORD

In a plpgsql function, I have a variable of type record:
my_rec RECORD;
This record contains a row from an arbitrary table, so I do not know the columns before it is executed.
However, I do have the name of at least one of the columns available as a varchar.
The question is: How do I retrieve the value for a given column from my_rec?
Use hstore to work with records with dynamic columns in PL/PgSQL functions:
CREATE EXTENSION hstore;
CREATE OR REPLACE FUNCTION test_fn(col_name text) RETURNS text AS $$
DECLARE
input_row record;
col_value text;
BEGIN
SELECT INTO input_row
*
FROM ( VALUES ('a','b','c',1) ) AS dummyrow(col1,col2,col3,intcol);
SELECT INTO col_value
hstore(input_row) -> col_name;
RETURN col_value;
END;
$$ LANGUAGE 'plpgsql';
hstore is an extension, but it's an extension that's been bundled with PostgreSQL since 8.3 and has been installable using CREATE EXTENSION since 9.1. The record-to-hstore conversion has been supported since something like 8.4 or 9.0.
I don't know of a way to do this in plpgsql. I did a bit of testing for you and tried to make a "EXECUTE SELECT" solution work, such as:
EXECUTE 'select $1.' || quote_ident(the_param) USING my_rec INTO my_var;
This does not work for me and I get:
could not identify column "{{param_value here}}" in record data type
Here is a very similar question from a few years ago saying that it is not possible with plpgsql. Per it's suggestion, it appears that it should be possible with some other languages. Quoting Tom Lane's answer:
There is no way to do that in plpgsql. You could do it in the other PLs
(eg plperl, pltcl) since they are not as strongly typed as plpgsql.

Passing a record as function argument PL/pgSQL

First I am really new to pl/pgsql. Need it for a project.
I am stuck with this (simplified) problem.
My db schema has a n to m relationship (author, books, author_books)
Now I want to have a pl/psgsql function insert_book. (I do know that all authors are definitely already in the author table, so I just want to pass their primary keys).
This function outline is what I have in mind.
create or replace function insert_book(book_to_insert book, authors integer[])
returns void as $$
begin
-- insert book into table books
-- for each author add an entry to author_books table
end;
$$ language plpgsql;
As arguments I thought to pass a record of type book and the authors that wrote it. But how exactly would this work? I googled quite a bit and can't seem to figure this out...
Question 1: Is the function outline "correct"/does it make sense?
Question 2: How to insert record book into table book? Do I have to go over all fields of book (title, isbn, publisher,...) and add them to an INSERT INTO statement or is there a "smarter" way?
Question 3: How would I call my function insert_book? I found this example here (http://dbaspot.com/postgresql/206142-passing-record-function-argument-pl-pgsql.html), but that doesn't really help me. For testing purposes I am using the shell, but later on we will use Java with JDBC.
Thank you very much for your help.
Using unnest() and a data-modifying CTE (requires Postgres 9.1 or later), this can be a simple SQL query:
WITH x AS (SELECT '(1,foo_book)'::book AS _book
, '{1,2,3}'::int[] AS _authors)
, y AS (
INSERT INTO book -- no column list, correct due to composite type
SELECT (x._book).*
FROM x
RETURNING book_id
)
INSERT INTO author_book (book_id, author_id)
SELECT y.book_id, unnest(x._authors)
FROM x,y; -- CROSS JOIN ok, only 1 row for x and y
The first CTE x is just for simplified data input and not strictly needed.
SQL Fiddle.
As to your questions:
Question 1: Is the function outline "correct"/does it make sense?
Might be easier to pass base types instead of the composite type book, but it is a perfectly valid approach. You have to know your way around the syntax for complex types, though. For instance, note the parenthesis around the name in my example: (x._book).*.
A plpgsql function could look like this:
CREATE OR REPLACE FUNCTION f_insert_book(_book book, _authors integer[])
RETURNS void AS
$func$
BEGIN
WITH y AS (
INSERT INTO book b
SELECT (_book).*
RETURNING b.book_id
)
INSERT INTO author_book (book_id, author_id)
SELECT y.book_id, unnest(_authors)
FROM y;
END
$func$ LANGUAGE plpgsql;
Question 2: How to insert record book into table book? (...) or is there a "smarter" way?
The smarter way is to decompose the composite type with (variable_name).*.
As the type is guaranteed to match the table (being derived from it), this is one of the rare cases, where it is perfectly ok, not to provide a column list for the INSERT command in persisted code.
Question 3: How would I call my function insert_book? ...
SELECT f_insert_book('(1,foo_book)'::book, '{1,2,3}'::int[]);
Within other plpgsql functions, use PERFORM instead of SELECT if you don't provide a target (INTO foo) for the (non-existing) results.
Passing JSON datatype (Postgresql 9.2 or higher):
CREATE OR REPLACE FUNCTION f_insert_book(_book json, _authors json)
RETURNS void AS
$$
BEGIN
-- insert book into table books
Insert into books values select * from json_populate_recordset(null:book, _book);
-- for each author add an entry to author_books table
Insert into authors values select * from json_populate_recordset(null:authors, _authors);
end;
$$ language plpgsql;