Passing a row type to jsonb_to_recordset syntax errors - postgresql

I'm trying to work out how to expand a JSONB field with a jsonb_to_recordset and a custom type.
Postgres 11.4.
This is just a test case, but for the minute I'm trying to work out the syntax. I've got a table named data_file_info with a JSONB field named table_stats. The JSONB field always includes a JSON array with the same structure:
[
{"table_name":"Activity","record_count":0,"table_number":214},
{"table_name":"Assembly","record_count":1,"table_number":15},
{"table_name":"AssemblyProds","record_count":0,"table_number":154}
]
The following code works correctly:
from data_file_info,
jsonb_to_recordset(table_stats) as table_stats_splat (
table_name text,
record_count integer,
table_number integer
)
What I would like to do is pass in a custm type definition instead of the long-form column definition list shown above. Here's the matching type:
create type data.table_stats_type as (
table_name text,
record_count integer,
table_number integer)
Some examples I've seen, and the docs say, that you can supply a type name using a null:row_type casting in the first parameter to jsonb_to_recordset. The examples that I've found use in-line JSON, while I'm trying to pull stored JSON. I've made a few attempts, all have failed. Below are two of the trials, with errors. Can someone point me towards the correct syntax?
FAIL:
select table_stats_splat.*
from data_file_info,
jsonb_populate_recordset(null::table_stats_type, data_file_info) as table_stats_splat;
-- ERROR: function jsonb_populate_recordset(table_stats_type, data_file_info) does not exist
-- LINE 4: jsonb_populate_recordset(null::table_stats_type, dat...
^
-- HINT: No function matches the given name and argument types. You might need to add explicit type casts. (Line 4)
FAIL:
select *
from jsonb_populate_recordset(NULL::table_stats_type, (select table_stats from data_file_info)) as table_stats_splat;
-- ERROR: more than one row returned by a subquery used as an expression. (Line 2)
I'm doubtlessly missing something pretty obvious, and am hoping someone can suggest what that is.

Use the column as the second parameter:
select table_stats_splat.*
from data_file_info,
jsonb_populate_recordset(null::table_stats_type, table_stats) as table_stats_splat;

Related

ERROR: syntax error at or near "%" when creating a table column variable

I am trying to create a composite type which contains a variable of table column type as below
create type gp_core.rec_key_transaction as(
CODE_TRANSACTION transaction_.CODE_TRANSACTION%TYPE
);
I get the below error
ERROR: syntax error at or near "%"
LINE 2: ... TYPE_TRANSACTION TRANSACTION_.TYPE_TRANSACTION%TYPE
SQL state: 42601
Character: 99
I have no idea what could be the problem...
The reason for the error is, that the syntax simply isn't supported.
If you want to create type where one attribute is a record of a "table type", there is no need for the %type to begin with. For every table (and view) a type with the same name is created. So if transaction_.code_transaction is an existing table, you can create your type as:
create type gp_core.rec_key_transaction as
(
CODE_TRANSACTION transaction_.CODE_TRANSACTION
);
But I honestly fail to see the usefulness of this approach. Why not just use the "table type" directly instead of wrapping it with another type?

Postgres, query error: ERROR: operator does not exist: character varying = bigint?

I am trying to run this query:
select *
from my_table
where column_one=${myValue}
I get the following error in Datagrip:
[42883] ERROR: operator does not exist: character varying = bigint Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
Now, I have found this question, and I can fix the error by putting a string like this:
select *
from my_table
where column_one='123'
What I need is a way to pass in the '123' as a parameter. I usually do this ${myValue} and it works, but I am not sure how to keep my variable there as an input so I can run dynamic queries in code and let Postgres understand I want to pass in a string and not a number.
Any suggestions?
Here's a screenshot of how I am putting the parameter value in DataGrip...:
Ok, so, I just tried to put quotes in the data grip parameters input field for myValue #thirumal's answer things work. I didn't know I have to quote the value for it to work.
This is what it looks like:
Type cast ${myValue} using SQL Standard,
cast(${myValue} AS varchar)
or using Postgres Syntax:
${myValue}::varchar

Is "create domain" allowed with a base type of json (or jsonb)?

I have a custom type with code similar to the following (shortened for brevity):
create domain massFraction as jsonb (
value->'h' > 0 and value->'h' is not null
);
Running this provokes the following error:
ERROR: type modifier is not allowed for type "jsonb"
The documentation makes no mention of json being a disallowed base type for a domain.
https://www.postgresql.org/docs/12/sql-createdomain.html
I also thought that it was perhaps the "not null" part of the constraint, which the documentation mentions is "tricky". Additionally, I've tried similar statements without any json operators. All of these seem to be disallowed.
I am obnoxiously prone to not reading documentation, and worse still about understanding documentation when I do bother to read it... so does it not exist, or am I looking in the wrong places to see if this is permitted? Why is it not permitted?
You're missing a CHECK after jsonb:
create domain massFraction as jsonb check (
value->'h' > 0 and value->'h' is not null
);
This results in another error:
ERROR: 42883: operator does not exist: jsonb > integer
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
I believe what you want is
CREATE DOMAIN massFraction AS jsonb CHECK (
CASE jsonb_typeof(value->'h')
-- only cast when it is safe to do so
-- note: this will reject numeric values stored as text in the json object (eg '{"h": "1"}')
WHEN 'number' THEN (value->>'h')::integer > 0
ELSE false
END
)
The error you're getting comes from the fact that you input is parsed as
CREATE DOMAIN massFraction AS jsonb(<modifier>) -- like in varchar(10)
As an aside I would recommend against using camelCase in postgresql, as massFraction is the same as massfraction (unless quoted) and postgresql will use the lowercased form when reporting errors, hints, etc.

PostgreSQL: how to extract value attribute from XML

I am trying to convert the following SQL Server query to Postgresql
select CAST(CAST('<IncludeSettle/><StartTime value="2019-03-26 08:45:48.780"></StartTime>' as XML).value('(//StartTime/#value)[1]', 'datetime') AS varchar(40)) + ''')';
I have tried converting it to below and got back following error.
select unnest(xpath('//StartTime/#value', xmlparse(document '<IncludeSettle/><StartTime value="2019-03-26 08:45:48.780"></StartTime>')))
Error:
ERROR: invalid XML document
DETAIL: line 1: Extra content at the end of the document
<IncludeSettle/><StartTime value="2010-03-26 08:45:48.780"></StartTim
^
SQL state: 2200M
As a hack, I had made the following change to make it work.
select unnest(xpath('//StartTime/#value', xmlparse(document '<tempzz>'||'<IncludeSettle/><StartTime value="2019-03-26 08:45:48.780"></StartTime>'||'</tempzz>')))
Output for Postgresql:
2019-03-26 08:45:48.780
I am looking for a better solution. Any help really appreciated.
You can process that by adding the dummy root as you did. The value is already formatted as an ISO timestamp, so you can simply cast it to a timestamp:
But as there is no direct cast from xml to timestamp you need to cast the result of the xpath() to text first.
with data (input) as (
values ('<IncludeSettle/><StartTime value="2019-03-26 08:45:48.780"></StartTime>')
)
select (xpath('//StartTime/#value', ('<dummy_root>'||input||'</dummy_root>')::xml))[1]::text::timestamp
from data

How to use Postgresql ts_delete function

I am trying to use Postgresql Full Text Search. I read that the stop words (words ignored for indexing) are implemented via dictionary. But I would like to give the user a limited control over the stop words (insert new ones), so I grouped then in a table.
From the example below:
select strip(to_tsvector('simple', texto)) from longtxts where id = 23;
I can get the vector:
{'alta' 'aluno' 'cada' 'do' 'em' 'leia' 'livro' 'pedir' 'que' 'trecho' 'um' 'voz'}
And now I would like to remove the elements from the stopwords table:
select array(select palavra_proibida from stopwords);
That returns the array:
{a,as,ao,aos,com,default,e,eu,o,os,da,das,de,do,dos,em,lhe,na,nao,nas,no,nos,ou,por,para,pra,que,sem,se,um,uma}
Then, following documentation:
ts_delete(vector tsvector, lexemes text[]) tsvector remove any occurrence of lexemes in lexemes from vector ts_delete('fat:2,4 cat:3 rat:5A'::tsvector, ARRAY['fat','rat'])
I tried a lot. For example:
select ts_delete((select strip(to_tsvector('simple', texto)) from longtxts where id = 23), array[(select palavra_proibida from stopwords)]);
But I always receive the error:
ERROR: function ts_delete(tsvector, character varying[]) does not exist
LINE 1: select ts_delete((select strip(to_tsvector('simple', texto))...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Could anyone help me? Thanks in advance!
ts_delete was introduced in PostgreSQL 9.6. Based on the error message, you're using an earlier version. You may try select version(); to be sure.
When you land on the PostgreSQL online documentation with a web search, it may correspond to any version. The version is in the URL and there's a "This page in another version" set of links at the top of each page to help switching to the equivalent doc for a different version.