My Oracle 12c Database characterset is WE8ISO8859P1
I have a table with column as NCLOB , and want to extract the data 3 different encoding
BASE64
HEX
UNICODE
I tried writing some function , the output is correct , but why new line is present in the encoded string.
Please find the details of the function as below
WITH
FUNCTION EncodeBASE64(InClearChar IN CLOB) RETURN CLOB IS
dest_lob BLOB;
lang_context INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
dest_offset INTEGER := 1;
src_offset INTEGER := 1;
read_offset INTEGER := 1;
warning INTEGER;
ClobLen INTEGER := DBMS_LOB.GETLENGTH(InClearChar);
amount INTEGER := 1440; -- must be a whole multiple of 3
-- size of a whole multiple of 48 is beneficial to get NEW_LINE after each 64 characters
buffer RAW(1440);
res CLOB := EMPTY_CLOB();
BEGIN
IF InClearChar IS NULL OR NVL(ClobLen, 0) = 0 THEN
RETURN NULL;
ELSIF ClobLen <= 24000 THEN
RETURN UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(InClearChar)));
END IF;
-- UTL_ENCODE.BASE64_ENCODE is limited to 32k/(3/4), process in chunks if bigger
DBMS_LOB.CREATETEMPORARY(dest_lob, TRUE);
DBMS_LOB.CONVERTTOBLOB(dest_lob, InClearChar, DBMS_LOB.LOBMAXSIZE, dest_offset, src_offset,
DBMS_LOB.DEFAULT_CSID, lang_context, warning);
LOOP
EXIT WHEN read_offset >= dest_offset;
DBMS_LOB.READ(dest_lob, amount, read_offset, buffer);
res := res || UTL_RAW.CAST_TO_VARCHAR2(UTL_ENCODE.BASE64_ENCODE(buffer));
read_offset := read_offset + amount;
END LOOP;
DBMS_LOB.FREETEMPORARY(dest_lob);
RETURN res;
END EncodeBASE64;
FUNCTION castToHex(InClearChar IN NCLOB) RETURN CLOB IS
dest_lob BLOB;
lang_context INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
dest_offset INTEGER := 1;
src_offset INTEGER := 1;
read_offset INTEGER := 1;
warning INTEGER;
ClobLen INTEGER := DBMS_LOB.GETLENGTH(InClearChar);
amount INTEGER := 1440; -- must be a whole multiple of 3
-- size of a whole multiple of 48 is beneficial to get NEW_LINE after each 64 characters
buffer RAW(1440);
res CLOB := EMPTY_CLOB();
BEGIN
IF InClearChar IS NULL OR NVL(ClobLen, 0) = 0 THEN
RETURN NULL;
ELSIF ClobLen <= 24000 THEN
RETURN RAWTOHEX(UTL_RAW.CAST_TO_RAW(InClearChar));
END IF;
-- UTL_ENCODE.BASE64_ENCODE is limited to 32k/(3/4), process in chunks if bigger
DBMS_LOB.CREATETEMPORARY(dest_lob, TRUE);
DBMS_LOB.CONVERTTOBLOB(dest_lob, InClearChar, DBMS_LOB.LOBMAXSIZE, dest_offset, src_offset,
DBMS_LOB.DEFAULT_CSID, lang_context, warning);
LOOP
EXIT WHEN read_offset >= dest_offset;
DBMS_LOB.READ(dest_lob, amount, read_offset, buffer);
res := res || RAWTOHEX(buffer);
read_offset := read_offset + amount;
END LOOP;
DBMS_LOB.FREETEMPORARY(dest_lob);
RETURN res;
END castToHex;
SELECT EncodeBASE64('uživaÄ Ê Í Õ Ø A B C D Etele, kteří temp in the town A B C D Etele, kteří') as encodedBASE64
from dual;
OUTPUT
encodedBASE64 -> dcW+aXZhw4Qgw4ogw40gw5Ugw5ggQSBCIEMgRCBFdGVsZSwga3RlxZnDrSB0ZW1w
IGluIHRoZSB0b3duICBBIEIgQyBEIEV0ZWxlLCBrdGXFmcOt
Question 1 - When I use this encodedBASE64 online to convert to String, it is returning the text correctly , but why new line is present in the encoded string , even If I remove the new line output is not changed
Question 2 - How to convert this string to unicode format ,please help
I have stored procedure in postgres with a variable like
DECLARE
totLen BYTEA;
BEGIN
totLen = E'\\x000034';
....
totLen must be exactly 3 bytes an I have to sum other value like
totLen = totLen + 1;
I try totLen = totLen + E'\x01' but doesn't work.
What's the correct solution?
This is a function that treats the three bytes from offset offs of the bytea value b as three-byte big-endian integer and adds i to that number:
CREATE OR REPLACE FUNCTION add(b bytea, offs integer, i integer) RETURNS bytea
LANGUAGE plpgsql IMMUTABLE STRICT AS
$$DECLARE
result integer := get_byte(b, offs) * 65536 +
get_byte(b, offs + 1) * 256 +
get_byte(b, offs + 2) +
i;
BEGIN
IF result > 16777215 THEN
RAISE EXCEPTION 'addition overflows';
END IF;
RETURN set_byte(
set_byte(
set_byte(
b,
offs,
result / 65536
),
offs + 1,
(result % 65536) / 256
),
offs + 2,
result % 256
);
END;$$;
I'm new to writing triggers and functions in Postgres.
I have written a function that changes prices to .99 or .00 whenever a price is put into the database.
CREATE OR REPLACE FUNCTION public.cents(price numeric)
RETURNS numeric
LANGUAGE plpgsql
LEAKPROOF
AS $function$
DECLARE
dollars text;
cents text;
new_price numeric;
BEGIN
dollars := split_part(price::text, '.', 1);
cents := split_part(price::text, '.', 2);
IF cents != '00' THEN cents := '99';
ENDIF;
new_price := dollars || '.' || cents;
RETURN new_price;
END;
$function$
I've read the doc on triggers and these examples seem more complex.
I'm not sure I understand how to create a trigger that will run this function specifically whenever a record in the price column is updated.
Does this look correct? The price column isn't mentioned in the trigger..
CREATE OR REPLACE FUNCTION public.cents()
RETURNS trigger
LANGUAGE plpgsql
LEAKPROOF
AS $tr_cents$
DECLARE
dollars text;
cents text;
new_price numeric;
BEGIN
dollars := split_part(OLD::text, '.', 1);
cents := split_part(OLD::text, '.', 2);
IF cents != '00' THEN cents := '99';
ENDIF;
new_price := dollars || cents;
RETURN new_price;
END;
$tr_cents$;
CREATE TRIGGER tr_cents BEFORE INSERT OR UPDATE ON microwaves
FOR EACH ROW EXECUTE PROCEDURE cents();
The examples in the docs also use RETURN NEW but I'm not exactly sure how that would work with my code, or if it's necessary?
Assuming missing information:
price is defined numeric NOT NULL.
There is a CHECK constraint enforcing positive prices.
Postgres 9.5. (Solution should work for Postgres 9.0+.)
I read your objective like this:
Leave numbers without (significant) fractional digits (.00) and change all others to .99.
See below about "without (significant) fractional digits" or .00 ...
If that's all the trigger does, the most efficient way is to place the condition in a WHEN clause to the trigger itself. The manual:
In a BEFORE trigger, the WHEN condition is evaluated just before the
function is or would be executed, so using WHEN is not materially
different from testing the same condition at the beginning of the trigger function.
(There is more, read the manual.)
This way, the trigger function is not even called if not needed. The logic can be radically simplified:
CREATE OR REPLACE FUNCTION tr_cents()
RETURNS trigger AS
$tr_cents$
BEGIN
-- only called WHEN (NEW.price % 1 IN (.00, .99)
NEW.price := trunc(NEW.price) + .99;
RETURN NEW;
END
$tr_cents$ LANGUAGE plpgsql LEAKPROOF;
CREATE TRIGGER microwaves_cents
BEFORE INSERT OR UPDATE ON microwaves
FOR EACH ROW
WHEN ((NEW.price % 1) <> ALL ('{.00,.99}'::numeric[]))
EXECUTE PROCEDURE tr_cents();
Note that the trigger kicks in for INSERT and UPDATE with illegal price values. Not just
whenever a record in the price column is updated.
You need RETURN NEW; at the end of the trigger function or the operation on the row will be cancelled. The manual:
A trigger function must return either NULL or a record/row value having exactly the structure of the table the trigger was fired for.
You don't need the function public.cents() at all for this.
Test case
CREATE TABLE microwaves (m_id serial PRIMARY KEY, price numeric);
INSERT INTO microwaves (m_id, price) VALUES
(1, 0.00)
, (2, 0.01)
, (3, 0.02)
, (4, 0.99)
, (5, 1.00)
, (6, 1.01)
, (7, 1.02)
, (8, 1.99)
, (9, 12.34);
UPDATE microwaves SET price = 2.0 WHERE m_id = 7;
UPDATE microwaves SET price = 2.5 WHERE m_id = 8;
UPDATE microwaves SET price = 5.99 WHERE m_id = 9;
TABLE microwaves;
Result:
m_id | price
------+-------
1 | 0.00
2 | 0.99
3 | 0.99
4 | 0.99
5 | 1.00
6 | 1.99
7 | 2.0
8 | 2.99
9 | 5.99
Data type numeric and scale
.. and why your function public.cents(price numeric) is a trap.
Scale being the number of decimal fractional digits.
numeric is an arbitrary precision type. It preserves literal digits exactly as entered - unless you specify precision and scale for the type. Like: numeric(10,2). The manual:
Specifying:
NUMERIC
without any precision or scale creates a column in which numeric
values of any precision and scale can be stored, up to the
implementation limit on precision. A column of this kind (numeric
without precision and scale) will not coerce input values to any
particular scale, whereas numeric columns with a declared scale will
coerce input values to that scale.
Leading zeroes are never stored, but trailing zeroes in the fractional part are kept this way, even if insignificant. The manual can easily be misread in this respect, further down:
Numeric values are physically stored without any extra leading or trailing zeroes.
Note the word "extra". Meaning, Postgres will not add trailing zeros, but it will keep the ones you added - even if those are completely insignificant for the numeric value.
You need to be aware of this when converting numeric to text. A check for "00" in the fractional part will work for numeric with a configured scale like numeric (9,2). But it is unreliable for plain numeric like you use in your function. Consider:
SELECT (numeric(9,2) '1')::numeric AS num_cast_from_num_with_scale
, numeric '1.00' AS num_with_scale
, numeric '1' AS num_without_scale;
num_cast_from_num_with_scale | num_with_scale | num_without_scale
------------------------------+----------------+-------------------
1.00 | 1.00 | 1
This way, insignificant trailing zeros become significant. And I seriously doubt that's how it's supposed to be. The test IF cents != '00' ... in your function public.cents(price numeric) is a loaded footgun. It may work as expected while you pass values from a numeric(9,2) column, but "suddenly" break once you use values from other sources.
You described return value as numeric, but return a string by fact. Also several type conversions not a good point. There is more easy way. F. ex.
CREATE OR REPLACE FUNCTION cents(price numeric) RETURNS numeric AS
$BODY$
begin
IF price IS NOT NULL then
IF price % 1 != 0 then
price := floor(price) + 0.99;
end IF;
END IF;
RETURN price;
end;
$BODY$ LANGUAGE plpgsql;
To execute such update on any insert/update need:
CREATE TABLE test (
price numeric
);
CREATE FUNCTION price_update() RETURNS trigger AS $price_update$
BEGIN
IF NEW.price IS NOT NULL THEN
NEW.price = public.cents(NEW.price);
END IF;
RETURN NEW;
END;
$price_update$ LANGUAGE plpgsql;
CREATE TRIGGER on_price_update BEFORE INSERT OR UPDATE ON test
FOR EACH ROW EXECUTE PROCEDURE price_update();
Lets check:
=# insert into test (price) values (2), (1.1), (5);
INSERT 0 3
=# select * from test;
price
-------
2
1.99
5
(3 rows)
=# update test set price = 5.01 where price = 5;
UPDATE 1
=# select * from test;
price
-------
2
1.99
5.99
(3 rows)
=# update test set price = 3 where price = 1.99;
UPDATE 1
=# select * from test;
price
-------
2
5.99
3
(3 rows)
In PostgreSQL, how to generate random unique integer number for column,
return which not exits in table column?
See the pseudo_encrypt function, which implements a permutation based on the Feistel network technique. Combined with a postgres sequence, this guarantees unicity of the result, as well as randomness to the human eye.
Example:
CREATE OR REPLACE FUNCTION pseudo_encrypt(VALUE int) returns int AS $$
DECLARE
l1 int;
l2 int;
r1 int;
r2 int;
i int:=0;
BEGIN
l1:= (VALUE >> 16) & 65535;
r1:= VALUE & 65535;
WHILE i < 3 LOOP
l2 := r1;
r2 := l1 # ((((1366 * r1 + 150889) % 714025) / 714025.0) * 32767)::int;
l1 := l2;
r1 := r2;
i := i + 1;
END LOOP;
RETURN ((r1 << 16) + l1);
END;
$$ LANGUAGE plpgsql strict immutable;
create sequence seq maxvalue 2147483647;
create table tablename(
id int default pseudo_encrypt(nextval('seq')::int),
[other columns]
);
A variant with a 64-bit output space can be found at: pseudo_encrypt() function in plpgsql that takes bigint.
EDIT: pseudo_encrypt implements only one permutation, and it does not accept a user-supplied key. If you prefer having your own permutations, depending on secret keys, you may consider skip32 (a 32-bit block cipher based on Skipjack, with 10 bytes wide keys).
A plpgsql function (ported from Perl/C) is available at:
https://wiki.postgresql.org/wiki/Skip32
I am trying to convert hex to decimal using PostgreSQL 9.1
with this query:
SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
I get the following error:
ERROR: invalid input syntax for type numeric: " "
What am I doing wrong?
Ways without dynamic SQL
There is no cast from hex numbers in text representation to a numeric type, but we can use bit(n) as waypoint. There are undocumented casts from bit strings (bit(n)) to integer types (int2, int4, int8) - the internal representation is binary compatible. Quoting Tom Lane:
This is relying on some undocumented behavior of the bit-type input
converter, but I see no reason to expect that would break. A possibly
bigger issue is that it requires PG >= 8.3 since there wasn't a text
to bit cast before that.
integer for max. 8 hex digits
Up to 8 hex digits can be converted to bit(32) and then coerced to integer (standard 4-byte integer):
SELECT ('x' || lpad(hex, 8, '0'))::bit(32)::int AS int_val
FROM (
VALUES
('1'::text)
, ('f')
, ('100')
, ('7fffffff')
, ('80000000') -- overflow into negative number
, ('deadbeef')
, ('ffffffff')
, ('ffffffff123') -- too long
) AS t(hex);
int_val
------------
1
15
256
2147483647
-2147483648
-559038737
-1
Postgres uses a signed integer type, so hex numbers above '7fffffff' overflow into negative integer numbers. This is still a valid, unique representation but the meaning is different. If that matters, switch to bigint; see below.
For more than 8 hex digits the least significant characters (excess to the right) get truncated.
4 bits in a bit string encode 1 hex digit. Hex numbers of known length can be cast to the respective bit(n) directly. Alternatively, pad hex numbers of unknown length with leading zeros (0) as demonstrated and cast to bit(32). Example with 7 hex digits and int or 8 digits and bigint:
SELECT ('x'|| 'deafbee')::bit(28)::int
, ('x'|| 'deadbeef')::bit(32)::bigint;
int4 | int8
-----------+------------
233503726 | 3735928559
bigint for max. 16 hex digits
Up to 16 hex digits can be converted to bit(64) and then coerced to bigint (int8, 8-byte integer) - overflowing into negative numbers in the upper half again:
SELECT ('x' || lpad(hex, 16, '0'))::bit(64)::bigint AS int8_val
FROM (
VALUES
('ff'::text)
, ('7fffffff')
, ('80000000')
, ('deadbeef')
, ('7fffffffffffffff')
, ('8000000000000000') -- overflow into negative number
, ('ffffffffffffffff')
, ('ffffffffffffffff123') -- too long
) t(hex);
int8_val
---------------------
255
2147483647
2147483648
3735928559
9223372036854775807
-9223372036854775808
-1
-1
uuid for max. 32 hex digits
The Postgres uuid data type is not a numeric type. But it's the most efficient type in standard Postgres to store up to 32 hex digits, only occupying 16 bytes of storage. There is a direct cast from text to uuid (no need for bit(n) as waypoint), but exactly 32 hex digits are required.
SELECT lpad(hex, 32, '0')::uuid AS uuid_val
FROM (
VALUES ('ff'::text)
, ('deadbeef')
, ('ffffffffffffffff')
, ('ffffffffffffffffffffffffffffffff')
, ('ffffffffffffffffffffffffffffffff123') -- too long
) t(hex);
uuid_val
--------------------------------------
00000000-0000-0000-0000-0000000000ff
00000000-0000-0000-0000-0000deadbeef
00000000-0000-0000-ffff-ffffffffffff
ffffffff-ffff-ffff-ffff-ffffffffffff
ffffffff-ffff-ffff-ffff-ffffffffffff
As you can see, standard output is a string of hex digits with typical separators for UUID.
md5 hash
This is particularly useful to store md5 hashes:
SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash;
md5_hash
--------------------------------------
02e10e94-e895-616e-8e23-bb7f8025da42
See:
What is the optimal data type for an MD5 field?
You have two immediate problems:
to_number doesn't understand hexadecimal.
X doesn't have any meaning in a to_number format string and anything without a meaning apparently means "skip a character".
I don't have an authoritative justification for (2), just empirical evidence:
=> SELECT to_number('123', 'X999');
to_number
-----------
23
(1 row)
=> SELECT to_number('123', 'XX999');
to_number
-----------
3
The documentation mentions how double quoted patterns are supposed to behave:
In to_date, to_number, and to_timestamp, double-quoted strings skip the number of input characters contained in the string, e.g. "XX" skips two input characters.
but the behavior of non-quoted characters that are not formatting characters appears to be unspecified.
In any case, to_number isn't the right tool for converting hex to numbers, you want to say something like this:
select x'deadbeef'::int;
so perhaps this function will work better for you:
CREATE OR REPLACE FUNCTION hex_to_int(hexval varchar) RETURNS integer AS $$
DECLARE
result int;
BEGIN
EXECUTE 'SELECT x' || quote_literal(hexval) || '::int' INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
Then:
=> select hex_to_int('DEADBEEF');
hex_to_int
------------
-559038737 **
(1 row)
** To avoid negative numbers like this from integer overflow error, use bigint instead of int to accommodate larger hex numbers (like IP addresses).
pg-bignum
Internally, pg-bignum uses the SSL library for big numbers. This method has none of the drawbacks mentioned in the other answers with numeric. Nor is it slowed down by plpgsql. It's fast and it works with a number of any size. Test case taken from Erwin's answer for comparison,
CREATE EXTENSION bignum;
SELECT hex, bn_in_hex(hex::cstring)
FROM (
VALUES ('ff'::text)
, ('7fffffff')
, ('80000000')
, ('deadbeef')
, ('7fffffffffffffff')
, ('8000000000000000')
, ('ffffffffffffffff')
, ('ffffffffffffffff123')
) t(hex);
hex | bn_in_hex
---------------------+-------------------------
ff | 255
7fffffff | 2147483647
80000000 | 2147483648
deadbeef | 3735928559
7fffffffffffffff | 9223372036854775807
8000000000000000 | 9223372036854775808
ffffffffffffffff | 18446744073709551615
ffffffffffffffff123 | 75557863725914323415331
(8 rows)
You can get the type to numeric using bn_in_hex('deadbeef')::text::numeric.
If anybody else is stuck with PG8.2, here is another way to do it.
bigint version:
create or replace function hex_to_bigint(hexval text) returns bigint as $$
select
(get_byte(x,0)::int8<<(7*8)) |
(get_byte(x,1)::int8<<(6*8)) |
(get_byte(x,2)::int8<<(5*8)) |
(get_byte(x,3)::int8<<(4*8)) |
(get_byte(x,4)::int8<<(3*8)) |
(get_byte(x,5)::int8<<(2*8)) |
(get_byte(x,6)::int8<<(1*8)) |
(get_byte(x,7)::int8)
from (
select decode(lpad($1, 16, '0'), 'hex') as x
) as a;
$$
language sql strict immutable;
int version:
create or replace function hex_to_int(hexval text) returns int as $$
select
(get_byte(x,0)::int<<(3*8)) |
(get_byte(x,1)::int<<(2*8)) |
(get_byte(x,2)::int<<(1*8)) |
(get_byte(x,3)::int)
from (
select decode(lpad($1, 8, '0'), 'hex') as x
) as a;
$$
language sql strict immutable;
Here is a version which uses numeric, so it can handle arbitrarily large hex strings:
create function hex_to_decimal(hex_string text)
returns text
language plpgsql immutable as $pgsql$
declare
bits bit varying;
result numeric := 0;
exponent numeric := 0;
chunk_size integer := 31;
start integer;
begin
execute 'SELECT x' || quote_literal(hex_string) INTO bits;
while length(bits) > 0 loop
start := greatest(1, length(bits) - chunk_size);
result := result + (substring(bits from start for chunk_size)::bigint)::numeric * pow(2::numeric, exponent);
exponent := exponent + chunk_size;
bits := substring(bits from 1 for greatest(0, length(bits) - chunk_size));
end loop;
return trunc(result, 0);
end
$pgsql$;
For example:
=# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015
Here is a poper way to convert hex to string... then you can check whether it's a numric type or not
SELECT convert_from('\x7468697320697320612076657279206C6F6E672068657820737472696E67','utf8')
returns
this is a very long hex string
Here is a other version which uses numeric, so it can handle arbitrarily large hex strings:
create OR REPLACE function hex_to_decimal2(hex_string text)
returns text
language plpgsql immutable as $pgsql$
declare
bits bit varying;
result numeric := 0;
begin
execute 'SELECT x' || quote_literal(hex_string) INTO bits;
while length(bits) > 0 loop
result := result + (substring(bits from 1 for 1)::bigint)::numeric * pow(2::numeric, length(bits) - 1);
bits := substring(bits from 2 for length(bits) - 1);
end loop;
return trunc(result, 0);
end
$pgsql$;
For example:
=# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015
For example:
=# select hex_to_decimal('5f68e8131ecf80000');
110000000000000000000
Here is another implementation:
CREATE OR REPLACE FUNCTION hex_to_decimal3(hex_string text)
RETURNS numeric
LANGUAGE plpgsql
IMMUTABLE
AS $function$
declare
hex_string_lower text := lower(hex_string);
i int;
digit int;
s numeric := 0;
begin
for i in 1 .. length(hex_string) loop
digit := position(substr(hex_string_lower, i, 1) in '0123456789abcdef') - 1;
if digit < 0 then
raise '"%" is not a valid hexadecimal digit', substr(hex_string_lower, i, 1) using errcode = '22P02';
end if;
s := s * 16 + digit;
end loop;
return s;
end
$function$;
It is a straightforward one that works digit by digit, using the position() function to compute the numeric value of each character in the input string. Its benefit over hex_to_decimal2() is that it seems to be much faster (4x or so for md5()-generated hex strings).
CREATE OR REPLACE FUNCTION numeric_from_bytes(bytea)
RETURNS numeric
LANGUAGE plpgsql
AS $$
declare
bits bit varying;
result numeric := 0;
exponent numeric := 0;
bit_pos integer;
begin
execute 'SELECT x' || quote_literal(substr($1::text,3)) into bits;
bit_pos := length(bits) + 1;
exponent := 0;
while bit_pos >= 56 loop
bit_pos := bit_pos - 56;
result := result + substring(bits from bit_pos for 56)::bigint::numeric * pow(2::numeric, exponent);
exponent := exponent + 56;
end loop;
while bit_pos >= 8 loop
bit_pos := bit_pos - 8;
result := result + substring(bits from bit_pos for 8)::bigint::numeric * pow(2::numeric, exponent);
exponent := exponent + 8;
end loop;
return trunc(result);
end;
$$;
In a future PostgreSQL version, when/if Dean Rasheed's patch 0001-Add-non-decimal-integer-support-to-type-numeric.patch gets committed, this can be simplified:
CREATE OR REPLACE FUNCTION numeric_from_bytes(bytea)
RETURNS numeric
LANGUAGE sql
AS $$
SELECT ('0'||right($1::text,-1))::numeric
$$;