PostgreSQL: ERROR operator does not exist: || character varying - postgresql

I need to check the condition within function using string_agg() function and need to assign it to variable. After assigning I need to execute the variable with value.
Example:
create or replace function funct1(a int,b varchar)
returns void as
$$
declare
wrclause varchar := '';
sqlq varchar ;
t varchar;
begin
IF (b IS NOT NULL ) THEN
wrclause := 'AND b IN ('|| b || ')';
END IF;
sqlq := string_agg('select *, abcd as "D" from ' ||table_namess,' Union all ') as namess
from tablescollection2 ud
inner join INFORMATION_SCHEMA.Tables so on ud.table_namess = so.Table_name
WHERE cola NOT IN (SELECT cola FROM tablet WHERE colb = || a ||) || wrclause; /* Error occurred here at = || a */
raise info '%',sqlq;
execute sqlq into t;
raise info '%',t;
end;
$$
language plpgsql;
Calling Function:
select funct1(1,'1,2,3');
Error:
ERROR: operator does not exist: || integer

|| is an operator for catenating two pieces of text, it requires you to have text (or something convertible to text) both before and after the operator, like so:
select 'a' || 'b'
select 'a' || 3
So while these seem to be valid:
wrclause := 'AND b IN ('|| b || ')';
sqlq := string_agg('select *, abcd as "D" from ' ||table_namess,' Union all ') as namess
This is definitely not:
WHERE cola NOT IN (SELECT cola FROM tablet WHERE colb = || a ||) || wrclause;
What were you trying to achieve here?
It looks like you may be trying to construct a query dynamically. You need to remember that you cannot mix free text with SQL and expect Postgres to sort it out, no programming or query language does that.
If that's your intention, you should construct the query string first in its entirety (in a variable), and then call EXECUTE with it to have it interpreted.
Have a look at these:
Postgres Dynamic Query Function
PostgreSQL - dynamic value as table name

This piece contains the syntax error
... IN (SELECT cola FROM tablet WHERE colb = || a ||) || ...
PostgreSQL can understand this, but will try to search for unary prefix (and a postfix) || operator, which are not exist by default (they can be created however, but the error message says, that's not the case)
Edit:
F.ex. these are valid (predefined) unary operators on numbers:
SELECT |/ 25.0, -- prefix, square root, result: 5.0
5 !, -- postfix, factorial, result: 120,
# -5, -- prefix, absolute, result: 5
# -5 !; -- mixed, result: 120

Related

How to concate join condition based on input flag value inside cursor in DB2

I have a below SP which has sql query which need to refactor db2 query,in db2 i dont know how to concatenate the flag condition remaining query to main query.
CREATE PROCEDURE EMPLOYEE
(IN EMPID varchar(1000),
IN BFLAG char(3))
RESULT SETS 1
LANGUAGE SQL
P1: BEGIN
SET v_sql = 'select c.id,c.name from emp c'
IF BFLAG <> 'T' THEN
SET v_sql = v_sql ||
' left outer join dept U
where c.empid in (' || EMPID || ') ';
ELSE
SET v_sql = v_sql ||
' where c.empid in (' || EMPID || ') ';
END IF;
how to concatenate query in db2 based on flag value specified above condition.
DECLARE c_id CURSOR WITH RETURN FOR
select c.id,c.name from emp c;
Too many errors.
You should study how the compound-statement must be constructed.
Every variable must be declared.
The order of statements inside is significant: variable declarations first, then statements, cursors. SQL-procedure-statements are afterwards only.
You get erroneous dynamic statement if BFLAG <> 'T' is true: left join without conditions.
If the questions is how to use cursor with dynamic statements, then here is an example:
CREATE PROCEDURE TEST_DYNAMIC (IN P_REST varchar(1000))
DYNAMIC RESULT SETS 1
LANGUAGE SQL
P1: BEGIN
DECLARE v_sql VARCHAR (1000);
DECLARE v_s STATEMENT;
DECLARE v_c1 CURSOR WITH RETURN FOR v_s;
SET v_sql = 'select ' || P_REST;
PREPARE v_s FROM v_sql;
OPEN v_c1;
END
#
CALL TEST_DYNAMIC (' * FROM EMPLOYEE WHERE EMPNO IN (''000010'', ''000020'')')#
If not, then try to compile your routine without errors at least and show the runtime error / unexpected result if any...

Using array in dynamic commands inside PL/pgSQL function

In a PL/pgSQL function, I am creating a view using the EXECUTE statement. The where clause in the view takes as input some jenkins job names. These job names are passed to the function as a comma-separated string. They are then converted to an array so that they can be used as argument to ANY in the where clause. See basic code below:
CREATE OR REPLACE FUNCTION FETCH_ALL_TIME_AGGR_KPIS(jobs VARCHAR)
RETURNS SETOF GenericKPI AS $$
DECLARE
job_names TEXT[];
BEGIN
job_names = string_to_array(jobs,',');
EXECUTE 'CREATE OR REPLACE TEMP VIEW dynamicView AS ' ||
'with pipeline_aggregated_kpis AS (
select
jenkins_build_parent_id,
sum (duration) as duration
from test_all_finished_job_builds_enhanced_view where job_name = ANY (' || array(select quote_ident(unnest(job_names))) || ') and jenkins_build_parent_id is not null
group by jenkins_build_parent_id)
select ' || quote_ident('pipeline-job') || ' as job_name, b1.jenkins_build_id, pipeline_aggregated_kpis.status, pipeline_aggregated_kpis.duration FROM job_builds_enhanced_view b1 INNER JOIN pipeline_aggregated_kpis ON (pipeline_aggregated_kpis.jenkins_build_parent_id = b1.jenkins_build_id)';
RETURN QUERY (select
count(*) as total_executions,
round(avg (duration) FILTER (WHERE status = 'SUCCESS')::numeric,2) as average_duration
from dynamicView);
END
$$
LANGUAGE plpgsql;
The creation of the function is successful but an error message is returned when I try to call the function. See below:
eea_ci_db=> select * from FETCH_ALL_TIME_AGGR_KPIS('integration,test');
ERROR: malformed array literal: ") and jenkins_build_parent_id is not null
group by jenkins_build_parent_id)
select "
LINE 7: ...| array(select quote_ident(unnest(job_names))) || ') and jen...
^
DETAIL: Array value must start with "{" or dimension information.
CONTEXT: PL/pgSQL function fetch_all_time_aggr_kpis(character varying) line 8 at EXECUTE
It seems like there is something going wrong with quotes & the passing of an array of string. I tried all following options with the same result:
where job_name = ANY (' || array(select quote_ident(unnest(job_names))) || ') and jenkins_build_parent_id is not null
or
where job_name = ANY (' || quote_ident(job_names)) || ') and jenkins_build_parent_id is not null
or
where job_name = ANY (' || job_names || ') and jenkins_build_parent_id is not null
Any ideas?
Thank you
There is no need for dynamic SQL at all. There isn't even the need for PL/pgSQL to do this:
CREATE OR REPLACE FUNCTION FETCH_ALL_TIME_AGGR_KPIS(jobs VARCHAR)
RETURNS SETOF GenericKPI
AS
$$
with pipeline_aggregated_kpis AS (
select jenkins_build_parent_id,
sum (duration) as duration
from test_all_finished_job_builds_enhanced_view
where job_name = ANY (string_to_array(jobs,','))
and jenkins_build_parent_id is not null
group by jenkins_build_parent_id
), dynamic_view as (
select "pipeline-job" as job_name,
b1.jenkins_build_id,
pipeline_aggregated_kpis.status,
pipeline_aggregated_kpis.duration
FROM job_builds_enhanced_view b1
JOIN pipeline_aggregated_kpis
ON pipeline_aggregated_kpis.jenkins_build_parent_id = b1.jenkins_build_id
)
select count(*) as total_executions,
round(avg (duration) FILTER (WHERE status = 'SUCCESS')::numeric,2) as average_duration
from dynamic_view;
$$
language sql;
You could do this with PL/pgSQL as well, you just need to use RETURN QUERY WITH ....

PostgreSql pass array argument in WHERE NOT

I have a sql function that creates a materialized view:
CREATE FUNCTION reports_mt_views(exclude_ids int[]) RETURNS void AS
$BODY$
BEGIN
EXECUTE 'CREATE MATERIALIZED VIEW tx_materialized AS
SELECT tx.transaccion_id AS tx_transaccion_id,
...
WHERE creation_date > (current_date - interval '' 3 month '')
AND (account_id <> ALL (' || $1 || '))'
RETURN;
END;
$BODY$ LANGUAGE plpgsql STRICT;
I also tried as:
AND account_id NOT IN ' || $1|| ')'
But it does not work. When I execute:
SELECT reports_mt_views(ARRAY [1,2,8,538524]);
I have this error:
ERROR: operator does not exist: text || integer[]
LINE 171: AND (account_id <> ALL (' || $1 || '))'
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
There is not a problem with the array itself I tested it with a FOR loop and works. But into the condition it does not. What I have missed here?
The solution was to concanate the array as a text then it can be readby the clause where not in.
CREATE OR REPLACE FUNCTION pps.reports_mt_views(exclude_ids integer[])
...
DECLARE
in_accounts ALIAS FOR $1;
out_accounts TEXT;
BEGIN
out_accounts := in_accounts;
out_accounts := trim(leading '{' FROM out_accounts);
out_accounts := trim(trailing '}' FROM out_accounts);
And it can be used as:
WHERE (orden.cuenta_id NOT IN (' || out_accounts || '))

Passing parameters gives array error in Postgres 8.2 function

I've got this function
/*
#Function: valiDates [Avoid inserting data from invalid dates]
#Purpose: Providing a _TABLE and a date _COLUMN to be validated.
Given a _DATE (from script name) validate that a certain % of data (_TOLERANCE) belongs to the _INTERVAL of days specified.
*/
CREATE OR REPLACE FUNCTION valiDates(_date date, _table regclass, _column text, _interval integer, _tolerance real) RETURNS BOOLEAN AS
$$
DECLARE result boolean;
BEGIN
EXECUTE 'SELECT
(SELECT count(*) FROM ' || _table::regclass || '
WHERE ' || _column || ' BETWEEN '''|| _date || '''::date and ''' || _date || '''::date + INTERVAL ''' || _interval || 'days'')'
|| '/
(SELECT COUNT(*) FROM ' || _table::regclass || ')::real
> ' || _tolerance
INTO result;
RETURN result;
END
;
$$ LANGUAGE plpgsql;
It actually works in my PostgreSQL environment Version 9.1.13, but when I try to call this function on Dev Server (PostgreSQL 8.2) the following error appears:
array value must start with "{" or dimension information
It should work on 8.2 as described in the official documentation page.
This is how I'm calling the function:
select valiDates('2015-03-01','_table','_date',1,0.8);
I really don't know how to fix it, I've already tried calling the function with '_table'::regclass but it doesn't works either.
Your error message most probably comes from _date as parameter name. _date is an alias for date[] - the type name for an array of dates. Similar for _interval.
Very old versions of Postgres could misunderstand that first word _date as data type instead of a parameter name. Use a sane parameter name to fix this. It is never a good idea to use basic type names as identifier of any kind.
Your function audited, should work in Postgres 8.2 (untested):
CREATE OR REPLACE FUNCTION validates(_day date, _table regclass, _column text
, _days integer, _tolerance real)
RETURNS BOOLEAN AS
$func$
DECLARE result boolean;
BEGIN
EXECUTE 'SELECT
(SELECT count(*) FROM ' || _table || '
WHERE ' || quote_ident(_column) || ' BETWEEN '''
|| _day || '''::date and '''
|| _day || '''::date + ' || _days
|| ') >
(SELECT COUNT(*) FROM ' || _table || ') * ' || _tolerance
INTO result;
RETURN result;
END
$func$ LANGUAGE plpgsql;
Notes:
Fix parameter names as discussed.
That's nonsense: _table::regclass - _table already is of type regclass
On the other hand, this is suicidal: _column. Wide open for SQL injection. Use quote_ident(_column) instead.
You can just add date + integer to add days. Much simpler.
3 / 2 > 1.5 can be rewritten to 3 > 2 * 1.5. Shorter, cleaner, clearer, avoids rounding error and no need for cast.
Modern syntax
In Postgres 9.1+, this could be:
CREATE OR REPLACE FUNCTION validates(_day date, _tbl regclass, _col text
, _days int, _tolerance real
, OUT result boolean) AS
$func$
BEGIN
EXECUTE format(
'SELECT count((%I BETWEEN $1 AND $2) OR NULL) > count(*) * $3 FROM %s'
, _col, _tbl
)
USING _day, _day + _days, _tolerance
INTO result;
END
$func$ LANGUAGE plpgsql;
Or even cleaner in Postgres 9.4+ with the new aggregate FILTER clause:
'SELECT count(*) FILTER (WHERE %I BETWEEN $1 AND $2)
> count(*) * $3 FROM %s'
Explanation:
How can I simplify this game statistics query?
Thank you Erwin for your advice, I took most of it. Finally I realized that it was expecting an array because of the _table parameter (regclass type) I just needed to change it or a string (text).

Postgres Function returning no results but when the same query returns results outside function

I am running a simple postgres function to return the count of rows. I am able to run the same query outside function with the output of raise option , but the function doesn't return any rows. I have tried different ways to produce results but unable to. Please find my function below,
CREATE OR REPLACE FUNCTION my_schema.usp_spellcheck3(SearchORItems_WithPipe varchar, site varchar, lan varchar, rows_display integer)
RETURNS TABLE (docmnt int) AS $BODY$
DECLARE
arrSearchTerms text[];
NewTerm varchar;
i integer;
AltSearch_withComma varchar;
AltSearch_withPipe varchar;
strDidYouMean varchar;
dpDidYouMean double precision;
txtDidYouMean Text;
SearchORItems_withComma varchar;
SearchORItems varchar;
SearchORItem varchar;
ws varchar;
arrSearchORItems_withComma varchar[];
BEGIN
strDidYouMean = 'DidYouMeanRow';
dpDidYouMean = 0.0;
txtDidYouMean = 'DidYouMeanRow';
ws = '''' || '%' || site || '%' || '''' ;
RAISE NOTICE '%', ws;
SearchORItems = REPLACE(SearchORItems_WithPipe, '|', ',');
SELECT regexp_split_to_array(SearchORItems, ',') INTO arrSearchORItems_withComma;
RAISE NOTICE '%', SearchORItems;
FOR i IN 1 .. coalesce(array_upper(arrSearchORItems_withComma, 1), 1) LOOP
IF (i = 1) THEN
SearchORItems_withComma = '''' || arrSearchORItems_withComma[i] || '''';
ELSE
SearchORItems_withComma = SearchORItems_withComma||','||'''' || arrSearchORItems_withComma[i] || '''';
END IF;
END LOOP;
RAISE NOTICE '%',SearchORItems_withComma;
SELECT COUNT(*) INTO res_count
FROM (
SELECT 1 FROM my_schema.features f , my_schema.documents d
WHERE term IN (SearchORItems_withComma)
AND d.docid = f.docid
AND d.url LIKE ws
GROUP BY f.docid, d.url) t;
RAISE NOTICE '%', res_count;
SearchORItem = 'SELECT COUNT(*) INTO res_count
FROM (SELECT 1 FROM my_schema.features f , my_schema.documents d
WHERE term IN ('||SearchORItems_withComma||')
AND d.docid = f.docid AND d.url LIKE ' || ws ||'
GROUP BY f.docid, d.url) t';
RAISE NOTICE '%',SearchORItem;
END;
$BODY$ LANGUAGE SQL VOLATILE;
this is my query output :
NOTICE: '%uni%'
NOTICE: daniel,data
NOTICE: 'daniel','data'
NOTICE: 0
NOTICE: select count(*) into res_count
from ( select 1 from my_schema.features f , my_schema.documents d
where term in ('daniel','data')
and d.docid=f.docid and d.url like '%uni%'
group by f.docid,d.url)t
Total query runtime: 16 ms.
0 rows retrieved.
I dont know where I'm going wrong, any help would be appreciated .. Thanks..
The simple reason that nothing is returned is that you have no RETURN statements in your code. When a function RETURNS TABLE you need to explicitly put one or more RETURN NEXT or RETURN QUERY statements in the body of your code, with a final RETURN statement to indicate the end of the function. See the documentation here: http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html#PLPGSQL-STATEMENTS-RETURNING. What exactly you want to return is not clear but likely candidates are res_count and d.docid.
Other than that, your code could use a real clean-up reducing clutter like:
ws := '''%' || site || '%''' ;
instead of:
ws = '''' || '%' || site || '%' || '''' ;
and:
SELECT COUNT(*) INTO res_count
FROM my_schema.features f,
JOIN my_schema.documents d ON d.docid = f.docid
WHERE term IN (SearchORItems_withComma)
AND d.url LIKE ws
GROUP BY f.docid, d.url;
instead of:
SELECT COUNT(*) INTO res_count
FROM (
SELECT 1 FROM my_schema.features f , my_schema.documents d
WHERE term IN (SearchORItems_withComma)
AND d.docid = f.docid
AND d.url LIKE ws
GROUP BY f.docid, d.url) t;
And you should use the assignment operator (:=) instead of the equality operator in any plpgsql statement that is not a SQL statement.