How load whole content file in function? - postgresql-9.5

I write function which will add new version of file to the table. The user of function should be call update_file(path_to_file). I need load all content from file and store it to the table like:
insert into file(new_version, new_content);
Now I could find a way to read all content and put it to the table. The COPY command allows me get the file concatenate as sequence of lines.
I see only way to contact the lines, but I don't like this approach.
Is there some good solution?
Thanks.

I found the following solution, hope it will be helpful for someone.
CREATE OR REPLACE FUNCTION read_file_utf8(path CHARACTER VARYING)
RETURNS TEXT AS $$
DECLARE
var_file_oid OID;
var_record RECORD;
var_result BYTEA := '';
BEGIN
SELECT lo_import(path)
INTO var_file_oid;
FOR var_record IN (SELECT data
FROM pg_largeobject
WHERE loid = var_file_oid
ORDER BY pageno) LOOP
var_result = var_result || var_record.data;
END LOOP;
PERFORM lo_unlink(var_file_oid);
RETURN convert_from(var_result, 'utf8');
END;
$$ LANGUAGE plpgsql;

As another solution may be using of $tag$...$tag$ syntax. It is PostgreSQL's feature, that called 'dollar quoting', it allows you insert any text between tags without quoting. For more details, please visit https://www.postgresql.org/docs/9.5/static/sql-syntax-lexical.html, 4.1.2.4. Dollar-quoted String Constants

Related

Looking for multiple strings in variable stored procedure

I'm trying to write a stored procedure for cloned databases. I want to check the database_name variable for a specific string. Currently, this is what I've got:
IF ((CONTAINS(:database_name, 'STRING1'))=TRUE) THEN
RAISE clone_exception;
END IF;
IF ((CONTAINS(:database_name, 'STRING2'))=TRUE) THEN
RAISE clone_exception;
END IF;
IF ((CONTAINS(:database_name, 'STRING3'))=TRUE) THEN
RAISE clone_exception;
END IF;
I don't want to have to write 3 CONTAIN blocks I'd rather keep it compact and clean, is there a way I could get all 3 STRING checks in the same block of code? I've looked into using the IN operator but I can't find documentation that doesn't have examples using only a table query which doesn't really help me since this conditional is comparing only a variable and not a table column.
It is easy to achieve with LIKE ANY:
DECLARE
database_name TEXT := '...';
BEGIN
IF (:database_name ILIKE ANY ('%STRING1%','%STRING2%','%STRING3%') THEN
RAISE clone_exception:
END IF;
END;

Save and return multiple rows within function pl/sql oracle

declare
type t_trayIds is table of number(38,0) index by binary_integer;
v_trayIdsTable t_trayIds;
create or replace function F_getTrayIdByDiameter(v_diameterInCm tray.diameterincm%TYPE)
return t_trayIds
as
v_trayIdsTable t_trayIds := null;
begin
select t.trayid into v_trayIds from tray t
where t.diameterincm = v_diameterincm;
return v_trayIdsTable;
end;
So what I want is, to ask for all Tray IDs with a specific Diameter and store them in an Array or Table. In Java I used ArrayList. I want to return the Table in the end to pass the result onto another function. The above code doesn't seem to work. SQL Developer gives me a syntax error at the word create.
Can someone help?
Your code fails because you are mixing a declare section that must be followed by a begin section, with a "create or replace function" that is a standalone statement to create objects;
If you want to declare a PL/SQL table type and make it public,
you must put it in a package specification, so it can be visible by any function (I also declare here the function F_getTrayIdByDiameter, to make it visible):
CREATE OR REPLACE package utils is
type t_trayIds is table of number(38,0) index by binary_integer;
function F_getTrayIdByDiameter(v_diameterInCm tray.diameterincm%TYPE) return t_trayIds;
end utils;
/
besides, you can't use SELECT INTO syntax, because
select col into var
can be used only for single row, not for lists;
in PL/SQL, if you want to manage multiple rows, you have to use a cursor;
so, if you want to create your PL/SQL table, you can fetch your cursor and build your list (PL/SQL table);
so, your package body can be,
CREATE OR REPLACE package body utils is
function F_getTrayIdByDiameter(v_diameterInCm tray.diameterincm%TYPE) return t_trayIds is
v_trayIdsTable t_trayIds;
i number := 0;
cursor c is
select t.trayid from tray t
where t.diameterincm = v_diameterincm;
begin
for my_rec in c loop
v_trayIdsTable(i) := my_rec.trayid;
i := i + 1;
end loop;
return v_trayIdsTable;
end;
end utils;
/
Then, you can use your list in another function, or in an anonymous block, just for example:
declare
my_result utils.t_trayIds;
begin
my_result := utils.F_GETTRAYIDBYDIAMETER(20);
dbms_output.put_line(my_result(0));
end;
By starting with declare you are creating an anonymous block, which do not have return values. They just do stuff and quit. It sounds like you want to create a function instead.
First, to return a collection of trayids, you need to create a type to return. This has to be done at the schema level; it is an object in its own right. There are three kinds of collections in Oracle: nested tables, associative arrays ("index by" tables), and varrays. I pretty much never use varrays and I don't think you can use associative arrays like this (but I forget, this may have changed in recent versions of Oracle). So create your type:
create or replace type t_trayids as table of number;
Now create your function. The key here is you must use bulk collect to populate the array. This is vastly faster than creating a result set and looping over it.
create or replace function F_getTrayIdByDiameter(v_diameterInCm tray.diameterincm%TYPE)
return t_trayIds
as
v_trayIdsTable t_trayIds;
begin
select t.trayid bulk collect into v_trayIdsTable from tray t
where t.diameterincm = v_diameterincm;
return v_trayIdsTable;
end;
As Nicola points out, you can also create a package and declare the type inside the package specification. You can use associative arrays this way. Which approach depends on what you're trying to do.

Unable to use SET OF record or return table(col1,[col2]) return type in pgadmin 4 version 2?

Basically i want to split the string based on the delimiter for example :-
"this,is,foo" should return this is foo i.e; without the comma into separate records .
i am trying to use pgadmin 4 version 2.
this is my sample code below that i want in function dialog :-
create or replace function STRING_SPLIT(str text,delimiter character)
return SET OF record;
as
begin
return QUERY SELECT trim(a)
FROM unnest(string_to_array('john,smith,jones', ',')) AS a; //str, delim
end
i am basically trying to insert the expanded row array into set of records.
i tried creating table and return it in the return statement but could not specify the column name in function dialog of pgadmin also i could not find "set of record" return type in the same dialog box?
can anyone help?
In almost all cases, using returns table() is the better solution.
create or replace function STRING_SPLIT(str text,delimiter character)
return table(word text) --<< no ; here!!
as
$$ --<< you need quotes around the body
SELECT trim(a) as word
FROM unnest(string_to_array(text, delimiter)) AS t(a);
$$
language sql; --<< no PL/pgSQL required, SQL is enough
If you do want to use setof record then you always have to specify the column names when using the function:
create or replace function STRING_SPLIT(str text,delimiter character)
return setof record
as
$$ --<< you need quotes around the body
SELECT trim(a) as word
FROM unnest(string_to_array(text, delimiter)) AS t(a);
$$
language sql; --<< no PL/pgSQL required, SQL is enough
Then:
select *
from string_split('foo,bar',',') as t(word);
However, Postgres already has a built-in function for this:
select *
from regexp_split_to_table('foo,bar',',') as t(word);
Though this post is older, but needs to post an answer here. So it will help people like me. As I was looking for the same and found the answer.
in pgAdmin4 'setof' is not present in the return type. While creating function in pgAdmin using create function window, you need to select record as a return type.
Then click on the options and set "Returns a set?" to "Yes". like this
Then it will automatically take setof record as a return type and will show in code in sql viwer, but not in return type selected.
Then it will automatically take setof record as a return type and will show in code in sql viwer, but not in return type selected.

Insert an image in postgresql database

I would like to know How can I insert an image "bytea" into a table of my postgreSql database? I've been searching forums for hours and have seen the same question posted dozens of times, but yet to find a single answer. All I see is how to insert .jpeg's into an old column which isn't what I need.
Here's the database table:
create table category (
"id_category" SERIAL,
"category_name" TEXT,
"category_image" bytea,
constraint id_cat_pkey primary key ("id_category"))without oids;
and when I add a new line, it doesn't work :
insert into category(category_name,category_image) values('tablette', lo_import('D:\image.jpg'));
If the column type is bytea then you can simply use the 'pg_read_binary_file'.
Example: pg_read_binary_file('/path-to-image/')
check postgresql documentation of pg_read_binary_file
insert into category(category_name,category_image) values('tablette', bytea('D:\image.jpg'));
The above solution works if column type is bytea
insert into category(category_name,category_image) values('tablette', lo_import('D:\image.jpg'));
The above solution works if column type is oid i.e., Blob
insert into category(category_name,category_image) values('tablette',decode('HexStringOfImage',hex));
The above decode function take two parameters. First parameter is HexString of Image.The second parameter is hex by default.Decode function coverts the hexString to bytes and store in bytea datatype column in postgres.
None of the above example worked well for me and on top of that I needed to add many images at once.
Full working example (python 3) with explanations:
With get_binary_array we get the value of the image (or file) as a binary array, using its path and file name as parameter (ex: '/home/Pictures/blue.png').
With send_files_to_postgresql we send all the images at once.
I previously created the database with one sequential 'id' that will automatically be incremented (but you can use your own homemade id) and one bytea 'image' field
import psycopg2
def get_binary_array(path):
with open(path, "rb") as image:
f = image.read()
b = bytes(f).hex()
return b
def send_files_to_postgresql(connection, cursor, file_names):
query = "INSERT INTO table(image) VALUES (decode(%s, 'hex'))"
mylist = []
for file_name in file_names:
mylist.append(get_binary_array(file_name))
try:
cursor.executemany(query, mylist)
connection.commit() # commit the changes to the database is advised for big files, see documentation
count = cursor.rowcount # check that the images were all successfully added
print (count, "Records inserted successfully into table")
except (Exception, psycopg2.DatabaseError) as error:
print(error)
def get_connection_cursor_tuple():
connection = None
try:
params = config()
print('Connecting to the PostgreSQL database...')
connection = psycopg2.connect(**params)
cursor = connection.cursor()
except (Exception, psycopg2.DatabaseError) as error:
print(error)
return connection, cursor
connection, cursor = connect_db.get_connection_cursor_tuple()
img_names = ['./blue.png', './landscape.jpg']
send_files_to_postgresql(connection, cursor, img_names)
Something like this function (slightly adapted from here) could work out.
create or replace function img_import(filename text)
returns void
volatile
as $$
declare
content_ bytea;
loid oid;
lfd integer;
lsize integer;
begin
loid := lo_import(filename);
lfd := lo_open(loid,131072);
lsize := lo_lseek(lfd,0,2);
perform lo_lseek(lfd,0,0);
content_ := loread(lfd,lsize);
perform lo_close(lfd);
perform lo_unlink(loid);
insert into category values
('tablette',
content_);
end;
$$ language plpgsql
Use it like select * from img_import('D:\image.jpg');
or rewrite to procedure if feeling like it.
create below function:
create or replace function bytea_import(p_path text, p_result out bytea)
language plpgsql as $$
declare
l_oid oid;
begin
select lo_import(p_path) into l_oid;
select lo_get(l_oid) INTO p_result;
perform lo_unlink(l_oid);
end;$$;
and use like this:
insert into table values(bytea_import('C:\1.png'));
For Linux users this is how to add the path to the image
insert into blog(img) values(bytea('/home/samkb420/Pictures/Sam Pics/sam.png'));
create table images (imgname text, img bytea);
insert into images(imgname,img) values ('MANGO', pg_read_binary_file('path_of_image')::bytea);
Use SQL workbench - Database explorer - insert a row and follow the dialogue...
enter image description here

How do I get textual contents from BLOB in Oracle SQL

I am trying to see from an SQL console what is inside an Oracle BLOB.
I know it contains a somewhat large body of text and I want to just see the text, but the following query only indicates that there is a BLOB in that field:
select BLOB_FIELD from TABLE_WITH_BLOB where ID = '<row id>';
the result I'm getting is not quite what I expected:
BLOB_FIELD
-----------------------
oracle.sql.BLOB#1c4ada9
So what kind of magic incantations can I do to turn the BLOB into it's textual representation?
PS: I am just trying to look at the content of the BLOB from an SQL console (Eclipse Data Tools), not use it in code.
First of all, you may want to store text in CLOB/NCLOB columns instead of BLOB, which is designed for binary data (your query would work with a CLOB, by the way).
The following query will let you see the first 32767 characters (at most) of the text inside the blob, provided all the character sets are compatible (original CS of the text stored in the BLOB, CS of the database used for VARCHAR2) :
select utl_raw.cast_to_varchar2(dbms_lob.substr(BLOB_FIELD)) from TABLE_WITH_BLOB where ID = '<row id>';
SQL Developer provides this functionality too :
Double click the results grid cell, and click edit :
Then on top-right part of the pop up , "View As Text" (You can even see images..)
And that's it!
You can use below SQL to read the BLOB Fields from table.
SELECT DBMS_LOB.SUBSTR(BLOB_FIELD_NAME) FROM TABLE_NAME;
Use this SQL to get the first 2000 chars of the BLOB.
SELECT utl_raw.cast_to_varchar2(dbms_lob.substr(<YOUR_BLOB_FIELD>,2000,1)) FROM <YOUR_TABLE>;
Note: This is because, Oracle will not be able to handle the conversion of BLOB that is more than length 2000.
If you want to search inside the text, rather than view it, this works:
with unzipped_text as (
select
my_id
,utl_compress.lz_uncompress(my_compressed_blob) as my_blob
from my_table
where my_id='MY_ID'
)
select * from unzipped_text
where dbms_lob.instr(my_blob, utl_raw.cast_to_raw('MY_SEARCH_STRING'))>0;
I can get this to work using TO_CLOB (docs):
select
to_clob(BLOB_FIELD)
from
TABLE_WITH_BLOB
where
ID = '<row id>';
This works for me in Oracle 19c, with a BLOB field which larger the the VARCHAR limit. I get readable text (from a JSON-holding BLOB)
Barn's answer worked for me with modification because my column is not compressed. The quick and dirty solution:
select * from my_table
where dbms_lob.instr(my_UNcompressed_blob, utl_raw.cast_to_raw('MY_SEARCH_STRING'))>0;
I struggled with this for a while and implemented the PL/SQL solution, but later realized that in Toad you can simply double click on the results grid cell, and it brings up an editor with contents in text. (i'm on Toad v11)
In case your text is compressed inside the blob using DEFLATE algorithm and it's quite large, you can use this function to read it
CREATE OR REPLACE PACKAGE read_gzipped_entity_package AS
FUNCTION read_entity(entity_id IN VARCHAR2)
RETURN VARCHAR2;
END read_gzipped_entity_package;
/
CREATE OR REPLACE PACKAGE BODY read_gzipped_entity_package IS
FUNCTION read_entity(entity_id IN VARCHAR2) RETURN VARCHAR2
IS
l_blob BLOB;
l_blob_length NUMBER;
l_amount BINARY_INTEGER := 10000; -- must be <= ~32765.
l_offset INTEGER := 1;
l_buffer RAW(20000);
l_text_buffer VARCHAR2(32767);
BEGIN
-- Get uncompressed BLOB
SELECT UTL_COMPRESS.LZ_UNCOMPRESS(COMPRESSED_BLOB_COLUMN_NAME)
INTO l_blob
FROM TABLE_NAME
WHERE ID = entity_id;
-- Figure out how long the BLOB is.
l_blob_length := DBMS_LOB.GETLENGTH(l_blob);
-- We'll loop through the BLOB as many times as necessary to
-- get all its data.
FOR i IN 1..CEIL(l_blob_length/l_amount) LOOP
-- Read in the given chunk of the BLOB.
DBMS_LOB.READ(l_blob
, l_amount
, l_offset
, l_buffer);
-- The DBMS_LOB.READ procedure dictates that its output be RAW.
-- This next procedure converts that RAW data to character data.
l_text_buffer := UTL_RAW.CAST_TO_VARCHAR2(l_buffer);
-- For the next iteration through the BLOB, bump up your offset
-- location (i.e., where you start reading from).
l_offset := l_offset + l_amount;
END LOOP;
RETURN l_text_buffer;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('!ERROR: ' || SUBSTR(SQLERRM,1,247));
END;
END read_gzipped_entity_package;
/
Then run select to get text
SELECT read_gzipped_entity_package.read_entity('entity_id') FROM DUAL;
Hope this will help someone.
You can try this:
SELECT TO_CHAR(dbms_lob.substr(BLOB_FIELD, 3900)) FROM TABLE_WITH_BLOB;
However, It would be limited to 4000 byte
Worked for me,
select lcase((insert(
insert(
insert(
insert(hex(BLOB_FIELD),9,0,'-'),
14,0,'-'),
19,0,'-'),
24,0,'-'))) as FIELD_ID
from TABLE_WITH_BLOB
where ID = 'row id';
Use TO_CHAR function.
select TO_CHAR(BLOB_FIELD) from TABLE_WITH_BLOB where ID = '<row id>'
Converts NCHAR, NVARCHAR2, CLOB, or NCLOB data to the database character set. The value returned is always VARCHAR2.