Why can't I use a plsql argument in this where clause? - postgresql

I have a function below (is_organizer) that works, and lets me use this method as a computed field in Hasura. The function below (is_chapter_member) which is almost identical, doesn't work.
WORKS
CREATE OR REPLACE FUNCTION is_organizer(event_row events, hasura_session json)
RETURNS boolean AS $$
SELECT EXISTS (
SELECT 1
FROM event_organizers o
WHERE
o.user_id::text = hasura_session->>'x-hasura-user-id'
AND
(event_row.id = o.event_id OR event_row.event_template_id = o.event_template_id)
);
$$ LANGUAGE SQL STRICT IMMUTABLE;
BROKEN
CREATE OR REPLACE FUNCTION is_chapter_member(c chapters, hasura_session json)
RETURNS boolean AS $$
SELECT EXISTS (
SELECT 1
FROM chapter_members m
WHERE
m.user_id::text = hasura_session->>'x-hasura-user-id'
AND
c.chapter_id = m.chapter_id
);
$$ LANGUAGE SQL STRICT IMMUTABLE;
When attempting to add this function (not call it, just create it) Postgres gives me the following error:
ERROR: missing FROM-clause entry for table "c"
LINE 9: c.chapter_id = m.chapter_id
Why would a function param need a where clause? Table dumps below...
Table "public.chapters"
Column | Type | Collation | Nullable | Default
-----------------+--------------------------+-----------+----------+--------------------------------------
id | integer | | not null | nextval('chapters_id_seq'::regclass)
title | text | | not null |
slug | text | | not null |
description | jsonb | | |
avatar_url | text | | |
photo_url | text | | |
region | text | | |
maps_api_result | jsonb | | |
lat | numeric(11,8) | | |
lng | numeric(11,8) | | |
created_at | timestamp with time zone | | not null | now()
updated_at | timestamp with time zone | | not null | now()
deleted_at | timestamp with time zone | | |
Table "public.chapter_members"
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
user_id | integer | | not null |
chapter_id | integer | | not null |
created_at | timestamp with time zone | | not null | now()
updated_at | timestamp with time zone | | not null | now()
Table "public.events"
Column | Type | Collation | Nullable | Default
-------------------+-----------------------------+-----------+----------+---------------------------------------------------
id | integer | | not null | nextval('events_id_seq'::regclass)
event_template_id | integer | | not null |
venue_id | integer | | |
starts_at | timestamp without time zone | | not null |
duration | interval | | not null |
title | text | | |
slug | text | | |
description | text | | |
photo_url | text | | |
created_at | timestamp without time zone | | not null | now()
updated_at | timestamp without time zone | | not null | now()
deleted_at | timestamp without time zone | | |
ends_at | timestamp without time zone | | | generated always as (starts_at + duration) stored
Table "public.event_organizers"
Column | Type | Collation | Nullable | Default
-------------------+---------+-----------+----------+----------------------------------------------
id | integer | | not null | nextval('event_organizers_id_seq'::regclass)
user_id | integer | | not null |
event_id | integer | | |
event_template_id | integer | | |

This turned out to be using an incorrect column name in the broken function. chapter_id should have just been id on the c argument. I took Richard's prompt and tried putting parens around the arg like (c).chapter_id. This then correctly told me that chapter_id doesn't exist, and allowed me to fix the issue.

Related

PostgreSQL: manipulating the type of a PRIMARY KEY column

Here's the structure of my table:
\d trajectories
Table "postgres.trajectories"
Column | Type | Collation | Nullable | Default
------------+--------------------------+-----------+----------+---------
user_id | integer | | |
session_id | bigint | | not null |
timestamp | timestamp with time zone | | not null |
lat | double precision | | not null |
lon | double precision | | not null |
alt | double precision | | |
Indexes:
"trajectories_pkey" PRIMARY KEY, btree (session_id, "timestamp")
"trajec_idx" btree (user_id, "timestamp")
Sample data:
SELECT * FROM trajectories LIMIT 5;
user_id | session_id | timestamp | lat | lon | alt
---------+-------------------+------------------------+-----------+------------+-----
85 | 84020081204232933 | 2008-12-05 07:27:03+00 | 39.934484 | 116.430599 | 200
85 | 84020081204232933 | 2008-12-05 07:27:08+00 | 39.934486 | 116.430635 | 199
85 | 84020081204232933 | 2008-12-05 07:27:13+00 | 39.934493 | 116.430689 | 199
85 | 84020081204232933 | 2008-12-05 07:27:18+00 | 39.934468 | 116.430648 | 199
85 | 84020081204232933 | 2008-12-05 07:27:23+00 | 39.934467 | 116.430614 | 199
(5 rows)
I would like to convert the timestamp column to epoch and no longer in this datetime format.
That is a bad idea. It is much better to have timestamps in the database.
Anyway, here goes:
ALTER TABLE trajectories
ALTER timestamp TYPE double precision USING extract(epoch FROM timestamp);

Why don't columns with citext datatype is processed by presto?

I'm running pgsql queries on the sql console provided by presto-client connected to presto-server running on top of postgres. The resultset of the queries contain only the columns that aren't of citext type.
DataDetails Table Description:
Table "public.datadetails"
Column | Type | Modifiers | Storage | Stats target | Description
------------------+----------+------------------------------+----------+--------------+-------------
data_sequence_id | bigint | not null | plain | |
key | citext | not null | extended | |
uploaded_by | bigint | not null | plain | |
uploaded_time | bigint | not null | plain | |
modified_by | bigint | | plain | |
modified_time | bigint | | plain | |
retrieved_by | bigint | | plain | |
retrieved_time | bigint | | plain | |
file_name | citext | not null | extended | |
file_type | citext | not null | extended | |
file_size | bigint | not null default 0::bigint | plain | |
Indexes:
"datadetails_pk1" PRIMARY KEY, btree (data_sequence_id)
"datadetails_uk0" UNIQUE CONSTRAINT, btree (key)
Check constraints:
"datadetails_file_name_c" CHECK (length(file_name::text) <= 32)
"datadetails_file_type_c" CHECK (length(file_type::text) <= 2048)
"datadetails_key_c" CHECK (length(key::text) <= 64)
Query Result in Presto-Client:
presto:public> select * from datadetails;
data_sequence_id | uploaded_by | uploaded_time | modified_by | modified_time | retrieved_by | retrieved_time | file_size |
------------------+-------------+---------------+-------------+---------------+--------------+----------------+-----------+
2000000000007 | 15062270 | 1586416286363 | 0 | 0 | 0 | 0 | 61 |
2000000000011 | 15062270 | 1586416299159 | 0 | 0 | 15062270 | 1586417517045 | 36 |
(2 rows)
Query 20200410_130419_00017_gmjgh, FINISHED, 1 node
Splits: 17 total, 17 done (100.00%)
0:00 [2 rows, 0B] [10 rows/s, 0B/s]
In the above resultset it is evident that the columns with citext type are missing.
Does presto support the citext datatype or Is there any configuration to process the citext datatype using presto?
Postgres: PostgreSQL 9.4.0-relocatable (Red Hat 4.4.7-11), 64-bit
Presto-Server: presto-server-0.230
Presto-Client: presto-cli-332

Postgres: return true/false if matches found from inner join?

I have implemented a tagging system in my application, using Postgres 9.6. There are three tables.
Projects
Table "public.project"
Column | Type | Collation | Nullable | Default
-------------+-----------------------------+-----------+----------+---------------------------------
id | integer | | not null | nextval('tag_id_seq'::regclass)
name | character varying(255) | | not null |
user_id | integer | | |
Tags
Table "public.tag"
Column | Type | Collation | Nullable | Default
-------------+-----------------------------+-----------+----------+---------------------------------
id | integer | | not null | nextval('tag_id_seq'::regclass)
tag | character varying(255) | | not null |
user_id | integer | | |
is_internal | boolean | | not null | false
Project tags
Column | Type | Collation | Nullable | Default
------------------+-----------------------------+-----------+----------+-----------------------------------------
id | integer | | not null | nextval('project_tag_id_seq'::regclass)
tag_id | integer | | not null |
project_id | integer | | | |
user_id | integer | | not null |
Now I want to get a list of all the projects, annotated with a column that indicates (for a particular tag) whether it has that tag.
So I'd like the results to look like this:
id name has_favorite_tag
1 foo true
2 bar false
3 baz false
This is my query so far:
select project.*, CASE(XXXX) as has_project_tag
from project p
join (select * from project_tag where tag_id=1) pt on p.id=pt.project_id
I know that I want to use CASE to be true when the length of project_tag matches is greater than 0 - but how do I do this?
(In reality the project table has many more fields, of course.)
Here's a possibility (unfiltered for tag_id; add to inner select if necessary):
select project.*, exists(select * from project_tag where id=project.id) as has_project_tag from project;

Using variables in select (apostrophes needed)

psql (9.6.1, server 9.5.5)
employees
Column | Type | Modifiers | Storage | Stats target | Description
----------------+-----------------------------+-----------------------------------------------------------------+----------+--------------+---- ---------
employee_id | integer | not null default nextval('employees_employee_id_seq'::regclass) | plain | |
first_name | character varying(20) | | extended | |
last_name | character varying(25) | not null | extended | |
email | character varying(25) | not null | extended | |
phone_number | character varying(20) | | extended | |
hire_date | timestamp without time zone | not null | plain | |
job_id | character varying(10) | not null | extended | |
salary | numeric(8,2) | | main | |
commission_pct | numeric(2,2) | | main | |
manager_id | integer | | plain | |
department_id | integer
For self education I'd like to use a variable.
The result of this request would suit me:
hr=> select last_name, char_length(last_name) as Length from employees where substring(last_name from 1 for 1) = 'H' order by last_name;
last_name | length
-----------+--------
Hartstein | 9
Higgins | 7
Hunold | 6
(3 rows)
But for self education I'd like to use a variable:
\set chosen_letter 'H'
hr=> select last_name, char_length(last_name) as Length from employees where substring(last_name from 1 for 1) = :chosen_letter order by last_name;
ERROR: column "h" does not exist
LINE 1: ...ployees where substring(last_name from 1 for 1) = H order by...
^
Those apostrophes seems to ruin everything. And I can't cope with the problem.
Could you help me understand how to use variable to acquire the result as above?
Try using:
\set chosen_letter '''H'''

Postgres group by with aggerate (last comment in a conversation across all conversations)

I want to get the last comment in a conversation between two people.
My table structure as follows:
Table "public.comments"
Column | Type | Modifiers | Storage | Stats target | Description
-------------+-----------------------------+-------------------------------------------------------+----------+--------------+-------------
id | integer | not null default nextval('comments_id_seq'::regclass) | plain | |
body | text | not null | extended | |
target_id | integer | | plain | |
target_type | character varying(255) | | extended | |
created_at | timestamp without time zone | not null | plain | |
updated_at | timestamp without time zone | not null | plain | |
user_id | integer | | plain | |
My Attempt:
SELECT
comments.id,
max(SELECT id comments.created_at),
CASE
WHEN user_id = 1 THEN CONCAT(user_id,'_',target_id)
WHEN target_id = 1 THEN CONCAT(target_id,'_',user_id)
END
FROM comments
WHERE
comments.user_id = 1
OR
(comments.target_type = 'User'
AND
comments.target_id = 1)
GROUP BY
CASE
WHEN user_id = 1 THEN CONCAT(user_id,'_',target_id)
WHEN target_id = 1 THEN CONCAT(target_id,'_',user_id)
END
So I figured out how to group the comments but how to order by created_at and get the latest id and information is where I'm stuck.