Convert Hexadecimals to timestamp in postgres - postgresql

I have the following hexadecimal data that I want to convert using the postgres functions, the data they receive is the following:
#AVLData_TimeStamp #AVLData_GPSElement_Altitude
00000174C0FA7EA0 0140
And I want to convert them to the following data
#MOPO_FECHAHORA #MOPO_ALTITUD
2020-09-24 09:37:56.000 320
I have tried to do it using this function but have not succeeded
SELECT to_timestamp(#AVLData_TimeStamp)
I have two functions in SQLserver that do this but in postgres I don't know how to do it
First Function:
CREATE FUNCTION FUN_Unix_To_Date(
#FechaUnix CHAR(38)
)
RETURNS DATETIME
AS
BEGIN
DECLARE
#LocalTime BIGINT
,#Adjusted BIGINT
,#seconds BIGINT = #FechaUnix
SET #LocalTime = DATEDIFF(MS,GETDATE(),GETUTCDATE())
SET #Adjusted = #seconds - #LocalTime
RETURN (DATEADD(SECOND,CONVERT(BIGINT, LEFT(CAST(#Adjusted AS VARCHAR(100)),13))/1000, CAST('1970-01-01 00:00:00' AS DATETIME)))
END
Two Function:
CREATE FUNCTION HexadecimalToDec_v2(
#hexval CHAR(38)
)
RETURNS NUMERIC(38)
AS
BEGIN
DECLARE #i INT
,#digits INT
,#result NUMERIC
,#current_digit CHAR(1)
,#current_digit_dec NUMERIC
SET #digits = LEN(#hexval)
SET #i = 0
SET #result =0
WHILE #i <= #digits
BEGIN
SET #current_digit = SUBSTRING(#hexval, #i, 1)
IF #current_digit = 'A' OR
#current_digit = 'B' OR
#current_digit = 'C' OR
#current_digit = 'D' OR
#current_digit = 'E' OR
#current_digit = 'F'
SET #current_digit_dec = ASCII(#current_digit) - ASCII('A') + 10
ELSE
SET #current_digit_dec = CONVERT(INT,#current_digit)
SET #result = (#result * 16) + #current_digit_dec
SET #i = #i + 1
END
RETURN(#result)
END
and finally the function is used like this
#MOPO_FECHAHORA = (dbo.Unix_To_Date(dbo.FUN_HexadecimalToDec_v2(#AVLData_TimeStamp)))
And this is the result
#MOPO_FECHAHORA
2020-09-24 09:37:56.000
Thank you very much for your help.

Use the function from this answer to convert hexadecimal to decimal (you might want to use bigint rather than integer):
CREATE OR REPLACE FUNCTION hex_to_int(hexval text) RETURNS bigint
LANGUAGE plpgsql IMMUTABLE STRICT AS
$$DECLARE
result bigint;
BEGIN
EXECUTE 'SELECT x' || quote_literal(hexval) || '::bigint'
INTO result;
RETURN result;
END;$$;
The timestamp can then be converted with
to_timestamp(hex_to_int('00000174C0FA7EA0') / 1000.0)

Just to clarify the source of issue.
The problem is that the your value(s) is the "local" epoch but to_timestamp() function returns timestamp with timezone value. Lets try some example:
with t(x) as (values('2020-09-24 09:37:56'))
select
x::timestamp as srcts,
x::timestamptz as srctstz,
to_timestamp(extract(epoch from x::timestamp)) as cnvts,
to_timestamp(extract(epoch from x::timestamptz)) as cnvtstz
from t;
┌─[ RECORD 1 ]─────────────────────┐
│ srcts │ 2020-09-24 09:37:56 │
│ srctstz │ 2020-09-24 09:37:56+03 │
│ cnvts │ 2020-09-24 12:37:56+03 │ <- Here is the issue in our case
│ cnvtstz │ 2020-09-24 09:37:56+03 │
└─────────┴────────────────────────┘
As you can see the data source type is critical. That's why you got the increased (actually converted from UTC to the local time zone) value in the first try using #LaurenzAlbe answer.
To fix this issue you need to perform some "reverse" calculations:
with t(x) as (values('2020-09-24 09:37:56'))
select
x::timestamp as srcts,
x::timestamptz as srctstz,
to_timestamp(extract(epoch from x::timestamp)) as cnvts,
(to_timestamp(extract(epoch from x::timestamp)))::timestamptz at time zone 'utc' as cnvtsrecalc,
to_timestamp(extract(epoch from x::timestamptz)) as cnvtstz
from t;
┌─[ RECORD 1 ]┬────────────────────────┐
│ srcts │ 2020-09-24 09:37:56 │
│ srctstz │ 2020-09-24 09:37:56+03 │
│ cnvts │ 2020-09-24 12:37:56+03 │ <- Here is the issue in our case
│ cnvtsrecalc │ 2020-09-24 09:37:56 │ <- Issue fixed
│ cnvtstz │ 2020-09-24 09:37:56+03 │
└─────────────┴────────────────────────┘
The following function wraps all this logic including the conversion of the hex value to bigint:
create or replace function hex2ts(text)
returns timestamp
language sql
immutable
strict
as $$
select
(to_timestamp(('x' || lpad($1, 16, '0'))::bit(64)::bigint / 1000.0))::timestamptz at time zone 'utc'
$$;

Related

What is the data type of column_name in information_schema.columns?

What is the data type of column_name in information_schema.columns?
i tried this but didnt get
code:
CREATE OR REPLACE FUNCTION table_name_sunitest118(
tbl character varying)
RETURNS TABLE(column_name text)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
RETURN QUERY
EXECUTE FORMAT('SELECT column_name FROM information_schema.columns where table_name=$1')
using tbl;
END
$BODY$;
In general you can use pg_typeof() to get that information:
select pg_typeof(column_name) from information_schema.columns limit 1;
┌───────────────────────────────────┐
│ pg_typeof │
├───────────────────────────────────┤
│ information_schema.sql_identifier │
└───────────────────────────────────┘
(1 row)
The type is sql_identifier, and you can cast it to text with column_name::text in your function.
CREATE OR REPLACE FUNCTION table_name_sunitest118(
tbl character varying)
RETURNS TABLE(column_name text)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
RETURN QUERY
EXECUTE FORMAT(
'SELECT column_name::text FROM information_schema.columns where table_name=$1'
) using tbl;
END
$BODY$;
CREATE FUNCTION
select * from table_name_sunitest118('users');
┌─────────────┐
│ column_name │
├─────────────┤
│ email │
│ pass │
│ role │
│ id │
│ email │
└─────────────┘
(5 rows)
In MySQL, describe information_schema.columns says varchar(64).
The Postgres docs for the columns table says it is a sql_identifier defined as...
A character string. This type is used for SQL identifiers, the type character_data is used for any other kind of text data.
In psql you can use \gdesc command. It shows type on client side.
postgres=# select column_name from information_schema.columns \gdesc
┌─────────────┬──────┐
│ Column │ Type │
╞═════════════╪══════╡
│ column_name │ name │
└─────────────┴──────┘
(1 row)
pg_typeof function shows type on server side
postgres=# select pg_typeof(column_name) from information_schema.columns limit 1;
┌───────────────────────────────────┐
│ pg_typeof │
╞═══════════════════════════════════╡
│ information_schema.sql_identifier │
└───────────────────────────────────┘
(1 row)

Update an array in a PostgreSQL table using a function

I'm trying to update a PostgreSQL table using a function.
My function:
CREATE OR REPLACE FUNCTION update_array_words(varchar, varchar[], int[], int, int)
RETURNS int AS $$
DECLARE
passed int;
j int;
k int;
BEGIN
passed := 0;
j := $4 + $5;
k := 0;
FOR i IN $4..j LOOP
UPDATE tab_files
SET key_words[i] = $2[k], num_key_words[i] = $3[k]
WHERE path_to_file = $1;
END LOOP;
RETURN passed;
END;
$$
LANGUAGE plpgsql
;
For calling my function:
SELECT update_array_words('path_name_to_file', '{"susana"}', '{1}', 1, 1);
The problem is, when I do a simple select in my PostgreSQL command line, the data from the update is null.
My select:
SELECT * FROM tab_files;
Output:
key_words num_key_words
| [0:2]={marques,NULL,NULL} | | [0:2]={3,NULL,NULL} |
What's wrong with my code?
In PostgreSQL arrays index by default starts from 1. Thus $2[k] = $2[0] (because of k := 0;) = null. Same with $3[k].
It is also not good idea to update same row in the loop several times. The better way is to select fields values into local variables, change them and then update your table once.
Update: If I guessing correctly about the purpose of the function, it could be simplified to update columns in single step without loop:
UPDATE tab_files set
key_words = key_words[1:$4-1] || array_fill($2[k],array[$5-$4+1]) || key_words[$5+1:],
num_key_words = num_key_words[1:$4-1] || array_fill($3[k],array[$5-$4+1]) || num_key_words[$5+1:]
WHERE path_to_file = $1;
You can to experiment with this using simple example:
with t(x,s,e,v) as (values(array[1,2,3,4,5,6,7,8], 2, 5, 0))
select
*,
x[1:s-1] as head,
array_fill(v, array[e-s+1]) as changed_part,
x[e+1:] as tail,
x[1:s-1] || array_fill(v, array[e-s+1]) || x[e+1:] as final_result
from t;
Result:
┌───────────────────┬───┬───┬───┬──────┬──────────────┬─────────┬───────────────────┐
│ x │ s │ e │ v │ head │ changed_part │ tail │ final_result │
├───────────────────┼───┼───┼───┼──────┼──────────────┼─────────┼───────────────────┤
│ {1,2,3,4,5,6,7,8} │ 2 │ 5 │ 0 │ {1} │ {0,0,0,0} │ {6,7,8} │ {1,0,0,0,0,6,7,8} │
└───────────────────┴───┴───┴───┴──────┴──────────────┴─────────┴───────────────────┘
However the better way is to create more general function like
create function array_replace_series(
p_array anyarray,
p_value anyelement,
p_start int,
p_end int)
returns anyarray language sql immutable
as $$
select
p_array[1:p_start-1] ||
array_fill(p_value, array[p_end-p_start+1]) ||
p_array[p_end+1:]
$$;
and then use it in your update:
UPDATE tab_files set
key_words = array_replace_series(key_words, 'susana', 1, 1),
num_key_words = array_replace_series(num_key_words, 1, 1, 1)
WHERE path_to_file = 'path_name_to_file';
And of course you will be able to reuse this function in other tasks.

Simple string return function with default param passed as NULL, returns NULL instead of string

I have the following function:
CREATE OR REPLACE FUNCTION public.get_string(cmd_type text, udf_name text,
group_name character varying DEFAULT 'usage'::character varying)
RETURNS text
LANGUAGE plpgsql
AS $function$
BEGIN
return 'This is the string: '''|| group_name ||''''::text;
END;
$function$
When i call it like this:
select public.get_string('test', 'myudf!', group_name=>null::character varying);
It returns NULL.
I expect it to at least return:
This is the string: ''
However, when I call it like this:
select public.get_string('test', 'myudf!');
The I get the expected:
This is the string: 'usage'
Why does passing NULL to an optional parameter make the entire string NULL?
It is not mystic - any operation over NULL value is NULL again.
postgres=# select ('Hello' || null) is null ;
┌──────────┐
│ ?column? │
╞══════════╡
│ t │
└──────────┘
(1 row)
You should to use a coalesce function and sanitize expression against NULL value.
postgres=# select ('Hello' || coalesce(null,'')) ;
┌──────────┐
│ ?column? │
╞══════════╡
│ Hello │
└──────────┘
(1 row)
Maybe you know a Oracle database, where NULL and empty strings are equal. But it is true only for Oracle, elsewhere NULL is NULL and it is more aggressive.

Passing Dynamic number of Parameters to Function

I need to pass dynamic number of parameters to a function as well as their data types and then return a table having those parameters as fields.
Is that possible to do that in postgres?
Any idea or example is appreciated
Here is an example that could probably be improved.
Beware of SQL injection!
CREATE OR REPLACE FUNCTION create_table(
tabname text,
VARIADIC coldef text[]
) RETURNS void
LANGUAGE plpgsql STRICT AS
$$DECLARE
l integer;
i integer;
sql text;
sep text := '';
BEGIN
l := array_upper(coldef, 1);
IF l % 2 <> 0 THEN
RAISE EXCEPTION 'Number of arguments must be odd';
END IF;
sql := 'CREATE TABLE ' || quote_ident(tabname) || '(';
FOR i IN 1 .. l/2 LOOP
sql := sql || sep || quote_ident(coldef[2*i-1]) || ' ' || quote_ident(coldef[2*i]);
sep := ', ';
END LOOP;
sql := sql || ')';
EXECUTE sql;
END;$$;
It can be used like this:
test=> SELECT create_table('tabname', 'col1', 'int4', 'col2', 'text');
test=> \d tabname
Table "laurenz.tabname"
┌────────┬─────────┬───────────┐
│ Column │ Type │ Modifiers │
├────────┼─────────┼───────────┤
│ col1 │ integer │ │
│ col2 │ text │ │
└────────┴─────────┴───────────┘

Postgres overlap arrays from one column

I have a Table "A" with one column "col1" where each record is a array of integers.
col1
-----
{1,2,3,4}
{1,2,6,7}
{1,2,3,8,9}
I like to have one row as result which contains the overlap or intersect of all arrays in "col1".
select overlap(col1) from A;
result
-----
{1,2}
You should to define custom aggregate for this purpose:
CREATE OR REPLACE FUNCTION public.overlap_array_aggregate(anyarray, anyarray)
RETURNS anyarray
LANGUAGE plpgsql STRICT
AS $function$
BEGIN
RETURN ARRAY(SELECT unnest($1) INTERSECT SELECT unnest($2));
END;
$function$
CREATE AGGREGATE array_overlap_agg (
basetype = anyarray,
sfunc = overlap_array_aggregate,
stype = anyarray );
Then it is working as you expect:
postgres=# SELECT * FROM foo;
┌─────────────┐
│ a │
╞═════════════╡
│ {1,2,3,4} │
│ {1,2,6,7} │
│ {1,2,3,8,9} │
└─────────────┘
(3 rows)
postgres=# SELECT array_overlap_agg(a) FROM foo;
┌───────────────────┐
│ array_overlap_agg │
╞═══════════════════╡
│ {1,2} │
└───────────────────┘
(1 row)