How to export raster from PostGIS in one go? - postgresql

In the simplified canonical example (which I often see on forums and in the books) of a raster exporting script the OID (returned by lo_create(0), here is 9585208) has to be known before the penultimate script line (lo_export 9585208 'C:/temp/raster.png'):
SELECT oid, lowrite(lo_open(oid, 131072), img) As num_bytes
FROM (
VALUES (
lo_create(0),
(SELECT ST_AsPNG(rast)
FROM bag_o_rasters
LIMIT 1)
)
) As v(oid, img);
lo_export 9585208 'C:/temp/raster.png'
SELECT lo_unlink(9585208);
I have a hard time with figuring out how to make PSQL cli utility to run this script in one pass, i.e., how to communicate OID returned by lo_create(0) to lo_export command. It seems the lo_export is client side command and it is not able to digest any query results... please, help...

Finally, there is a PSQL command \gset which stores values into the script variables, the documentation says:
\gset sends the current query buffer to the server and stores the query's
output into psql variables. The query to be executed must return
exactly one row. Each column of the row is stored into a separate
variable, named the same as the column.
Here is correspondingly modified script:
SELECT lo_create(0) as blob_oid
\gset
SELECT oid, lowrite(lo_open(oid, 131072), img) As num_bytes
FROM (
VALUES (
:blob_oid,
(SELECT ST_AsPNG(rast)
FROM bag_o_rasters
LIMIT 1)
)
) As v(oid,img);
\lo_export :blob_oid 'C:/temp/raster.png'
SELECT lo_unlink(:blob_oid);

Related

How to get Databricks aes_encrypt to give the same output for the same input

I have a need to encrypt some data in Databricks. I'm currently using the built in aes_encrypt function. If I use the sql shown below, I get a distinct value for each record in the table. I do not get the same value for the encrypted value when the same input is used.
Is there a way to encrypt data in Databricks so the same input yields the same output?
drop table if exists encr;
create table encr as (
select
original_text,
base64(aes_encrypt(original_text,'abcdefabcdefabcdefabcdef')) as encrypted,
cast(aes_decrypt(unbase64(base64(aes_encrypt(original_text,'abcdefabcdefabcdefabcdef'))), 'abcdefabcdefabcdefabcdef') as string) as decrypted
from
my_table
)
;
Results:
select
count(*),
count(distinct original_text),
count(distinct encrypted)
from
encr
;
Setting the mode to 'ECB' gets the same output for the same input:
https://docs.databricks.com/sql/language-manual/functions/aes_encrypt.html
drop table if exists encr;
create table encr as (
select
original_text,
base64(aes_encrypt(original_text,'abcdefabcdefabcdefabcdef','ECB')) as encrypted,
cast(aes_decrypt(unbase64(base64(aes_encrypt(original_text,'abcdefabcdefabcdefabcdef'))), 'abcdefabcdefabcdefabcdef') as string) as decrypted
from
my_table
)
;

Postgres Functions: Getting the Return Table Column Details

I feel the need to get the column names and data types of the table returned by any function that has a 'record' return data type, because...
A key process in an existing SQL Server-based system makes use of a stored procedure that takes a user-defined function as a parameter. An initial step gets the column names and types of the table returned by the function that was passed as a parameter.
In Postgres 13 I can use pg_proc.prorettype and the corresponding pg_type to find functions that return record types...that's a start. I can also use pg_get_function_result() to get the string containing the information I need. But, it's a string, and while I ultimately will have to assemble a very similar string, this is just one application of the info. Is there a tabular equivalent containing (column_name, data_type, ordinal_position), or do I need to do that myself?
Is there access to a composite data type the system may have created when such a function is created?
One option that I think will work for me, but I think it's a little weird, is to:
> create temp table t as select * from function() limit 0;
then look that table up in info_schema.columns, assemble what I need and drop the temp table...putting all of this into a function.
You can query the catalog table pg_proc, which contains all the required information:
SELECT coalesce(p.na, 'column' || p.i),
p.ty::regtype,
p.i
FROM pg_proc AS f
CROSS JOIN LATERAL unnest(
coalesce(f.proallargtypes, ARRAY[f.prorettype]),
f.proargmodes,
f.proargnames
)
WITH ORDINALITY AS p(ty,mo,na,i)
WHERE f.proname = 'interval_ok'
AND coalesce(p.mo, 'o') IN ('o', 't')
ORDER BY p.i;

Postgresql & PgAdmin SIMPLE variable [duplicate]

How do I declare a variable for use in a PostgreSQL 8.3 query?
In MS SQL Server I can do this:
DECLARE #myvar INT
SET #myvar = 5
SELECT *
FROM somewhere
WHERE something = #myvar
How do I do the same in PostgreSQL? According to the documentation variables are declared simply as "name type;", but this gives me a syntax error:
myvar INTEGER;
Could someone give me an example of the correct syntax?
I accomplished the same goal by using a WITH clause, it's nowhere near as elegant but can do the same thing. Though for this example it's really overkill. I also don't particularly recommend this.
WITH myconstants (var1, var2) as (
values (5, 'foo')
)
SELECT *
FROM somewhere, myconstants
WHERE something = var1
OR something_else = var2;
There is no such feature in PostgreSQL. You can do it only in pl/PgSQL (or other pl/*), but not in plain SQL.
An exception is WITH () query which can work as a variable, or even tuple of variables. It allows you to return a table of temporary values.
WITH master_user AS (
SELECT
login,
registration_date
FROM users
WHERE ...
)
SELECT *
FROM users
WHERE master_login = (SELECT login
FROM master_user)
AND (SELECT registration_date
FROM master_user) > ...;
You could also try this in PLPGSQL:
DO $$
DECLARE myvar integer;
BEGIN
SELECT 5 INTO myvar;
DROP TABLE IF EXISTS tmp_table;
CREATE TABLE tmp_table AS
SELECT * FROM yourtable WHERE id = myvar;
END $$;
SELECT * FROM tmp_table;
The above requires Postgres 9.0 or later.
Dynamic Config Settings
you can "abuse" dynamic config settings for this:
-- choose some prefix that is unlikely to be used by postgres
set session my.vars.id = '1';
select *
from person
where id = current_setting('my.vars.id')::int;
Config settings are always varchar values, so you need to cast them to the correct data type when using them. This works with any SQL client whereas \set only works in psql
The above requires Postgres 9.2 or later.
For previous versions, the variable had to be declared in postgresql.conf prior to being used, so it limited its usability somewhat. Actually not the variable completely, but the config "class" which is essentially the prefix. But once the prefix was defined, any variable could be used without changing postgresql.conf
It depends on your client.
However, if you're using the psql client, then you can use the following:
my_db=> \set myvar 5
my_db=> SELECT :myvar + 1 AS my_var_plus_1;
my_var_plus_1
---------------
6
If you are using text variables you need to quote.
\set myvar 'sometextvalue'
select * from sometable where name = :'myvar';
This solution is based on the one proposed by fei0x but it has the advantages that there is no need to join the value list of constants in the query and constants can be easily listed at the start of the query. It also works in recursive queries.
Basically, every constant is a single-value table declared in a WITH clause which can then be called anywhere in the remaining part of the query.
Basic example with two constants:
WITH
constant_1_str AS (VALUES ('Hello World')),
constant_2_int AS (VALUES (100))
SELECT *
FROM some_table
WHERE table_column = (table constant_1_str)
LIMIT (table constant_2_int)
Alternatively you can use SELECT * FROM constant_name instead of TABLE constant_name which might not be valid for other query languages different to postgresql.
Using a Temp Table outside of pl/PgSQL
Outside of using pl/pgsql or other pl/* language as suggested, this is the only other possibility I could think of.
begin;
select 5::int as var into temp table myvar;
select *
from somewhere s, myvar v
where s.something = v.var;
commit;
I want to propose an improvement to #DarioBarrionuevo's answer, to make it simpler leveraging temporary tables.
DO $$
DECLARE myvar integer = 5;
BEGIN
CREATE TEMP TABLE tmp_table ON COMMIT DROP AS
-- put here your query with variables:
SELECT *
FROM yourtable
WHERE id = myvar;
END $$;
SELECT * FROM tmp_table;
True, there is no vivid and unambiguous way to declare a single-value variable, what you can do is
with myVar as (select "any value really")
then, to get access to the value stored in this construction, you do
(select * from myVar)
for example
with var as (select 123)
... where id = (select * from var)
You may resort to tool special features. Like for DBeaver own proprietary syntax:
#set name = 'me'
SELECT :name;
SELECT ${name};
DELETE FROM book b
WHERE b.author_id IN (SELECT a.id FROM author AS a WHERE a.name = :name);
As you will have gathered from the other answers, PostgreSQL doesn’t have this mechanism in straight SQL, though you can now use an anonymous block. However, you can do something similar with a Common Table Expression (CTE):
WITH vars AS (
SELECT 5 AS myvar
)
SELECT *
FROM somewhere,vars
WHERE something = vars.myvar;
You can, of course, have as many variables as you like, and they can also be derived. For example:
WITH vars AS (
SELECT
'1980-01-01'::date AS start,
'1999-12-31'::date AS end,
(SELECT avg(height) FROM customers) AS avg_height
)
SELECT *
FROM customers,vars
WHERE (dob BETWEEN vars.start AND vars.end) AND height<vars.avg_height;
The process is:
Generate a one-row cte using SELECT without a table (in Oracle you will need to include FROM DUAL).
CROSS JOIN the cte with the other table. Although there is a CROSS JOIN syntax, the older comma syntax is slightly more readable.
Note that I have cast the dates to avoid possible issues in the SELECT clause. I used PostgreSQL’s shorter syntax, but you could have used the more formal CAST('1980-01-01' AS date) for cross-dialect compatibility.
Normally, you want to avoid cross joins, but since you’re only cross joining a single row, this has the effect of simply widening the table with the variable data.
In many cases, you don’t need to include the vars. prefix if the names don’t clash with the names in the other table. I include it here to make the point clear.
Also, you can go on to add more CTEs.
This also works in all current versions of MSSQL and MySQL, which do support variables, as well as SQLite which doesn’t, and Oracle which sort of does and sort of doesn’t.
Here is an example using PREPARE statements. You still can't use ?, but you can use $n notation:
PREPARE foo(integer) AS
SELECT *
FROM somewhere
WHERE something = $1;
EXECUTE foo(5);
DEALLOCATE foo;
In DBeaver you can use parameters in queries just like you can from code, so this will work:
SELECT *
FROM somewhere
WHERE something = :myvar
When you run the query DBeaver will ask you for the value for :myvar and run the query.
Here is a code segment using plain variable in postges terminal. I have used it a few times. But need to figure a better way. Here I am working with string variable. Working with integer variable, you don't need the triple quote. Triple quote becomes single quote at query time; otherwise you got syntax error. There might be a way to eliminate the need of triple quote when working with string variables. Please update if you find a way to improve.
\set strainname '''B.1.1.7'''
select *
from covid19strain
where name = :strainname ;
In psql, you can use these 'variables' as macros. Note that they get "evaluated" every time they are used, rather than at the time that they are "set".
Simple example:
\set my_random '(SELECT random())'
select :my_random; -- gives 0.23330629315990592
select :my_random; -- gives 0.67458399344433542
this gives two different answers each time.
However, you can still use these as a valuable shorthand to avoid repeating lots of subselects.
\set the_id '(SELECT id FROM table_1 WHERE name = ''xxx'' LIMIT 1)'
and then use it in your queries later as
:the_id
e.g.
INSERT INTO table2 (table1_id,x,y,z) VALUES (:the_id, 1,2,3)
Note you have to double-quote the strings in the variables, because the whole thing is then string-interpolated (i.e. macro-expanded) into your query.

How to use psql command line to remotely save multiple FOR loop results to remote client files?

I have an anonymous function containing a query within a FOR loop that executes 100 times, and I need to save the 100 result sets as 100 files on the remote client (not on the server).
It seems like the psql \copy meta-command should be the way to do this, but I'm at a loss. Something of this form, maybe?
\copy (anonymous_function_w/_FOR_loop_here) to 'filename.txt'
where filename.txt is built from the FOR loop variable's value in each iteration. That's important - the files on the remote client need to be named based on the FOR loop's variable.
Is there any way to pull this off? I suppose an alternative approach would be to UNION all 100 query results into one big result, with the FOR loop's variable value in one field, and then use bash scripting to split it into 100 appropriately named files. But my bash skills are pretty lame. If psql can do the job directly that would be great.
EDIT: I should add that here's what the FOR loop variable looks like:
FOR rec IN SELECT DISTINCT county FROM voter.counties
so the file name would be built from rec.county + '.txt'
The typical approach to this is to use a SQL statement that generates the necessary statements, spool the output into a script file, then run that file.
Something like:
-- prepare for a "plain" output without headers or something similar
\a
\t
-- spool the output into export.sql
\o export.sql
select format('\copy (select * from some_table where county = %L) to ''%s.txt''', county, county)
from (select distinct county from voter.counties) t;
-- turn spooling off
\o
-- run the generated file
\i export.sql
So for each county name in voters.counties the export.sql will contain:
\copy (select * from some_table where county = 'foobar') to 'foobar.txt'

Query bytea field in postgres via command line

I have a table with a bytea field, and it would be convenient if I could do queries via the command line (or pgAdmin's query executor). I've got the hex value as a string. Is there a built in function to convert hex to bytea?
I'd like to do something like:
SELECT * FROM table WHERE my_bytea_field=some_???_function('fa26e312');
where 'fa26e312' is the hex value of the bytea field I want.
Note: this is just to be helpful while I'm developing / debugging things, I can do it via code but I'd like to be able to do it by hand in a query.
Try using built-in decode(string text, type text) function (it returns bytea). You can run queries via CLI using psql in non-interactive mode, that is with -c switch (there are some formatting options if you like):
psql -c "SELECT * FROM table WHERE my_bytea_field=decode('fa26e312', 'hex');"
Example:
CREATE TABLE test(id serial, my_bytea_field bytea);
INSERT INTO test (my_bytea_field) VALUES
(E'\\320\\170'::bytea),
(E'\\100\\070'::bytea),
(E'\\377\\377'::bytea);
psql -tc "SELECT * FROM test WHERE my_bytea_field=decode('ffff', 'hex');"
3 | \377\377
SELECT * FROM table WHERE my_bytea_field=E'\\xfa26e312';
Just as in the example in the Binary Data Types docs (note the E'\\x' prefix):
SELECT E'\\xDEADBEEF';
under psql console, you can see its bytea content by default, such as:
also you can query like this (such as in pg_admin) :
xxx=# select id, encode(code_hash, 'hex') from your_table where id = 1195;
id | encode
------+------------------------------------------------------------------
1195 | c5e5dcf215925f7ef4dfaf5f4b4f105bc321c02776d6e7d52a1db3fcd9d011a4