transform character varying [] to character varying postgresql - postgresql

I have multiple input files that I want to run through the same SQL file. Sometimes a column that I run a regular expression on is an character varying[] and sometimes that same column in a different file is a character varying without the []. I want to make sure that it does not matter whether the input file is a character varying with or without the [].
The following works for an individual file:
ALTER TABLE table ALTER COLUMN column TYPE character varying;
UPDATE table SET column = left(column , length(column )-1);
UPDATE table SET column = right(column , length(column )-1);
When changing the type from character varying[] to character varying it keeps the brackets that I have to remove with an update statement. I am not managing to create a solution that works for both input files.
I think this direction of thought might be the answer but PL SQL is difficult for me: http://www.postgresqltutorial.com/plpgsql-if-else-statements/ . Working on this at the moment but not sure if I am heading in the right direction:
DO $$
DECLARE
??? a integer := 10;
??? b integer := 10;
BEGIN
IF column === character varying[] THEN
ALTER TABLE table ALTER COLUMN column TYPE character varying;
UPDATE table SET column = left(column , length(column )-1);
UPDATE table SET column = right(column , length(column )-1);
ELSE
RAISE NOTICE 'column is a character varying';
END IF;
END $$;

Fixed it by doing the following:
ALTER TABLE elektriciteitskabel ALTER COLUMN link_href TYPE character varying;
UPDATE elektriciteitskabel SET link_href = replace(link_href, '{', '');
UPDATE elektriciteitskabel SET link_href = replace(link_href, '}', '');

Related

postgresql create function dynamically insert value into different table

I want to create a function that can dynamically insert any data type value into any table and multiple columns. But my function can only insert one value to one column:
CREATE OR REPLACE FUNCTION public.dynamic_insert(
tablename character varying,
columname character varying,
datatype character varying,
valuee text
)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE AS
$BODY$
begin
Execute format('insert into %I ('||columname||')
values (cast($1 as '||datatype||'))',tablename,columname);
end;
$BODY$;
Can any one help?
I don't understand your idea clearly. I guess that you want to write one function to insert into table with list of column and values you specify.
If I guess correctly. I suggest some function like this:
CREATE OR REPLACE FUNCTION public.dynamic_insert(
tablename character varying,
columname character varying[],
datatype character varying[],
valuee character varying[]
)
RETURNS void AS
$BODY$
DECLARE
var_sql varchar;
var_sql_column varchar := '';
var_sql_values varchar := '';
begin
var_sql_column := CONCAT('("',ARRAY_TO_STRING(columname, '","'),'")');
FOR i IN 1..ARRAY_LENGTH(columname,1)
LOOP
var_sql_values := var_sql_values || quote_literal(valuee[i]) || '::' || datatype[i] || ',';
END LOOP;
var_sql_values := CONCAT('(',regexp_replace(var_sql_values, ',$', '', 'g'),')');
var_sql := CONCAT('INSERT INTO ', quote_ident(tablename), var_sql_column, ' VALUES ', var_sql_values);
IF (var_sql IS NOT NULL) THEN
EXECUTE(var_sql);
END IF;
end;
$BODY$;
LANGUAGE 'plpgsql'
COST 100
VOLATILE;
Hopefully it matches your require.
p/s: with my experience, just choose one solution between Format, and String concatenation, don't use both in one query, it makes me hard to view and find bug.
------------- Update for question below -----------------------------------
For the Loop please refer the document here
Basically, it will run from 1 to n (with n is the number elements in array) and build a query.
One simple thing to view how it run is you can put some print out data into you function, such as RAISE. Please refer it document here

PostgreSQL Custom operator compare varchar and integer

-- PostgreSQL 9.6.2 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-17), 64-bit.
-- Installed from offical repository.
-- No any changes in postgresql.conf .
-- CentOS release 6.8.
-- User: postgres.
-- Used pgAdmin3 LTS by BigSQL.
-- No any unusual log in server.
I have many queries.
in that case I need compare character varying data type (may be a table field) with integer value.
--Result is True or False
select '10' = 10;
select '10' = '10';
select '10'::character varying = '10'::character varying;
select '10'::character varying = 'foo bar';
select '10'::character varying = 'foo bar'::character varying;
select 'foo bar' = 'foo bar';
select '10'::character varying = '10';
--Result is "operator does not exist: character varying = integer"
select '10'::character varying = 10;
so i create a custom operator for compare character varying and integer.
step 1: create simple function
CREATE OR REPLACE FUNCTION public.is_equal_char_int(character varying, integer) RETURNS boolean AS
$BODY$
BEGIN
IF $1 = $2::character varying THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
End;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
step 2: create new operator
CREATE OPERATOR public.=(
PROCEDURE = is_equal_char_int,
LEFTARG = character varying,
RIGHTARG = integer);
so i resoleved my problem and
select '10'::character varying = 10;
return true value.
and new problem is:
when i compare character varying value with unkown data type value, postgresql use my custom operator.
select '10'::character varying = 'foo bar';
result is :
invalid input syntax for integer: "foo bar"
select pg_typeof('foo bar');
return unkown data type.
and next step I create new operator for compare character varying and unkown data type.
Step 1:
CREATE OR REPLACE FUNCTION public.is_equal_char_unknown(character varying, unknown)
RETURNS boolean AS
$BODY$
BEGIN
IF $1 = $2::character varying THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
End;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
step 2:
CREATE OPERATOR public.=(
PROCEDURE = is_equal_char_unknown,
LEFTARG = character varying,
RIGHTARG = unknown);
when I run
select '10'::character varying = 'foo bar';
I give
ERROR: operator is not unique: character varying = unknown.
So I'm in a hole.
To understand how type resolution is done for operators in PostgreSQL, read the operator type resolution rules in the documentation.
In your special case, the following operators remain after step 3.a:
Your custom operator (character varying = integer).
character = character (implicit conversion from character varyingto character).
name = name (implicit conversion from character varyingto name).
text = text (implicit conversion from character varyingto text).
Rule 3.c then chooses your operator, because it is the only one with an exact type match. Without your operator, step 3.d would have chosen text = text, because text is the only preferred type of the string category.
What you are doing at the moment is discovering why certain operators are not defined in PostgreSQL, namely that defining new comparison operators for new type combinations results in ambiguities that lead to errors because PostgreSQL cannot decide which operator to use.
At the heart of the problem is PostgreSQL's ability to overload operators, i.e. to have several operators with the same name. Yet this is a good feature, and the system of casts and operators has been carefully balanced to make the experience as good as possible. The unknown type is also part of that system.
In other words, PostgreSQL is trying to guess what you mean, but this is not always possible. If you want to compare a (not unknown) string and a number, what do you want? Should they be compared as numbers or as strings? Should '010' be the same as 10 or not? PostgreSQL doesn't know what you mean and gives up.
There could be another option by defining how the cast between varchars and numerics should be made:
CREATE CAST (VARCHAR AS NUMERIC) WITH INOUT AS IMPLICIT;
This would make it possible to do comparisons like this:
SELECT '1'::character varying = 1::int;
> true
SELECT '01'::character varying = 1::int;
> true
SELECT '2'::character varying = 1::int;
> false
select '10'::character varying = 'foo bar';
> false
More about creating casts in postgresql here:
https://www.postgresql.org/docs/current/sql-createcast.html

DBMS_SQL Character constraint

I encountered the following error, i'm learning about DBMS_SQL.
I was playing around with the below code which drops a table and recreates a new table. I noticed for the recreation of the new table, i'm running into a character constraint. Just wondering what the best solution is to fix this.
I've tried to concatenating two strings but that didnt work.
'CREATE TABLE students_12345(s_id NUMBER, fname VARCHAR2(30),lname VARCHAR2(30),tname VARCHAR2(100), score NUMBER, exam_result VARCHAR2(6))';
Its probably something really simple but its taken me a couple of hours to get to this point in the code and my eyes are going square shaped at this point.
Thanks in advance!
Error starting at line : 48 in command -
EXECUTE RecreateTempTable('a')
Error report -
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at "SYS.RECREATETEMPTABLE", line 33
ORA-06512: at line 1
06502. 00000 - "PL/SQL: numeric or value error%s"
*Cause: An arithmetic, numeric, string, conversion, or constraint error
occurred. For example, this error occurs if an attempt is made to
assign the value NULL to a variable declared NOT NULL, or if an
attempt is made to assign an integer larger than 99 to a variable
declared NUMBER(2).
*Action: Change the data, how it is manipulated, or how it is declared so
that values do not violate constraints.
CREATE TABLE students_12345
(student_id NUMBER,
first_name VARCHAR2(30),
last_name VARCHAR2(30),
test_name VARCHAR2(100),
score NUMBER,
exam_result VARCHAR2(6));
/
-----------------------------------------------------------------------------
CREATE OR REPLACE PROCEDURE RecreateTempTable (
p_description IN VARCHAR2) IS
v_descrip VARCHAR2(100) := p_description;
v_cursor NUMBER;
v_createstring VARCHAR2(100);
v_dropstring VARCHAR2(100);
v_numrows INTEGER;
BEGIN
v_cursor := DBMS_SQL.OPEN_CURSOR;
v_dropstring := 'DROP TABLE students_12345';
BEGIN
-- parse the query using the parameter table name
DBMS_SQL.PARSE(v_cursor, v_dropString, DBMS_SQL.NATIVE);
-- execute the cursor query
v_numrows := DBMS_SQL.EXECUTE(v_cursor);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
v_createstring := 'CREATE TABLE students_12345(s_id NUMBER, fname VARCHAR2(30),lname VARCHAR2(30),tname VARCHAR2(100), score NUMBER)';
DBMS_SQL.PARSE(v_cursor, v_createstring, DBMS_SQL.NATIVE);
v_numrows := DBMS_SQL.EXECUTE(v_cursor);
DBMS_SQL.CLOSE_CURSOR(v_cursor);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(v_cursor);
RAISE;
END RecreateTempTable;
/
-------------------------------------------------------------------------------
EXECUTE RecreateTempTable('a');

Postgres- SQL state: 22004 - query string argument of EXECUTE is null

I have a table (named VGI_table) that contains a column (named match_tabl) which contains the names of other tables in the same database along with object_ids for those tables. I am trying to create a plpgsql function that loops through each row in the VGI_table and performs a query to retrieve an object from another table as shown below.
The function takes 4 parameters (all varchar), the first two are names of columns in the VGI_table, the third is the name of the VGI_table and the last parameter is the output.
vgi_match_id_col, vgi_match_table_col, vgi_table, output_table
The code for the function is shown below, ro is used to hold the first table query, match_row holds the output of the queried external table. Distance is an output created using the PostGIS st_distance function.
DECLARE
ro record;
match_row record;
distance float;
BEGIN
for ro in EXECUTE 'select gid, geom, '||vgi_match_id_col||' as match_id, '||vgi_match_table_col||' as match_table from '||vgi_table
LOOP
--raise notice '(%)', 'select geom from public.'||ro.match_table||' where gid = '||ro.match_id;
execute 'select geom from public.'||ro.match_table||' where gid = '||ro.match_id into match_row;
distance := st_distance(ro.geom, st_transform(match_row.geom,st_srid(ro.geom)));
EXECUTE 'INSERT INTO '||output_table||' VALUES('||ro.gid||', '||distance||')';
END LOOP;
The table being queried has no null values in the match_tabl column or the object_id colum. The code identifies ro.match_table and ro.match_id as null values when attempting to perform the EXECUTE statement. I even used the RAISE NOTICE function with the same string that is used in the EXECUTE statement and the correct query is returned. If I hard code the execute string with a predefined table_name and object id the script works fine. The link below is similar but I don't think it addresses my question. Thanks for the help.
Similar Question
Well, clearly something you're concatenating is null.
Use the format function instead, that way you'll get more useful info.
format('select geom from public.%I ....', ro.match_table);
Use EXECUTE ... USING ... to insert literals.
e.g.
EXECUTE format('INSERT INTO %I VALUES($1, $2)', output_table) USING (ro.gid, distance);
In postgres , If anything null is being passed in Dynamic DML , we are bound to get this issue."query string argument of EXECUTE is null"
You can use below sample steps for insert and update .
CREATE OR REPLACE FUNCTION samplefunc(
col1param character varying,
col2param character varying,
col3param character varying,
col4param character varying,
col5param character varying,
col6param character varying,
col7param character varying,
col8param character varying
RETURNS boolean AS
$BODY$
declare
begin
EXECUTE format( 'insert into '||tablename||' (id, col1, col2, col3, col4, col5)values($1,$2,$3,$4,$5)') USING col1param ,col2param,col3param,col4param,col5param;
EXECUTE format('update '||tablename||' set col1 =$1,col2 = $2,col3=$3,col4=$4,col5=$5
where col6=$6 and col7=$7 and col8=$8 ') using col1param,col2param,,col3param,col4param,col5param,col6param,col7param,col8param;
end

PostgreSQL: from OID to Bytea

We have decided to move from OIDs in our PostgreSQL 9.0 database and use bytea columns instead. I'm trying to copy the data from one column to the other, but I can't figure out the right query. This is the closest I've gotten to:
update user as thistable set pkcs_as_bytea = (select array_agg(mylargeobject.data) from
(select * from pg_largeobject where loid = thistable.pkcs12_as_oid order by pageno) as mylargeobject) where thistable.pkcs12 is not null
And that gives me the following error message:
ERROR: column "pkcs_as_bytea" is of type bytea but expression is of type bytea[]
What would be the right query then?
Another way which doesn't require a custom function is to use the loread(lo_open(...)) combination, like:
UPDATE user SET pkcs_as_bytea = loread(lo_open(pkcs12_as_oid, 262144), 1000000) WHERE thistable.pkcs12 IS NOT NULL
There is a problem with this code, the loread function requires as the second parameter the maximum number of bytes to read (the 1000000 parameter I used above), so you should use a really big number here if your data is big. Otherwise, the content will be trimmed after this many bytes, and you won't get all the data back into the bytea field.
If you want to convert from OID to a text field, you should also use a conversion function, as in:
UPDATE user SET pkcs_as_text = convert_from(loread(lo_open(pkcs12_as_oid, 262144), 1000000), 'UTF8')
(262144 is a flag for the open mode, 40000 in hexa, which means "open read-only")
Here is a stored procedure that does the magic:
CREATE OR REPLACE FUNCTION merge_oid(val oid)
returns bytea as $$
declare merged bytea;
declare arr bytea;
BEGIN
FOR arr IN SELECT data from pg_largeobject WHERE loid = val ORDER BY pageno LOOP
IF merged IS NULL THEN
merged := arr;
ELSE
merged := merged || arr;
END IF;
END LOOP;
RETURN merged;
END
$$ LANGUAGE plpgsql;
well, i did something like this. I have attachment table and content column with data in oid type. I migrated with four actions:
ALTER TABLE attachment add column content_bytea bytea
UPDATE attachment SET content_bytea = lo_get(content)
ALTER TABLE attachment drop column content
ALTER TABLE attachment rename column content_bytea to content
You need something like array_to_string(anyarray, text) for text arrays, but in this case an array_to_bytea(largeobjectarray) to concat all sections. You have to create this function yourself, or handle this in application logic.
This is what you can do.
--table thistable --
ALTER TABLE thistable add column se_signed_bytea bytea;
UPDATE thistable SET se_signed_bytea = lo_get(pkcs_as_bytea);
ALTER TABLE thistable drop column pkc`enter code here`s_as_bytea;
ALTER TABLE thistable rename column se_signed_bytea to pkcs_as_bytea;