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

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.

Related

"In Unicode programs, must have the same structure layout, irrespective of the length" error

I'm now getting this error after making a small mod to a working program. The structures have the same type (though the tables are different) but I get this error? I've looked at similar postings but couldn't find an answer to this.
Codes snippets included.
* ---
,begin of TY_RESB_CDC
,MANDT type MANDT
,RSNUM type RSNUM
,RSPOS type RSPOS
,RSART type RSART
,UDATE type CDDATUM
,UTIME type CDUZEIT
.include type ZBW_MATERIAL_RESVN_RESB.
types: end of TY_RESB_CDC
,TT_RESB_CDC type hashed table of TY_RESB_CDC
with unique key RSNUM RSPOS RSART
,TT_RESB_STD type standard table of TY_RESB_CDC
with empty key
---
,LT_RESB_CDC type TT_RESB_CDC
,WA_RESB_CDC type TY_RESB_CDC "like LINE OF LT_RESB_CDC
,LT_RESB_STD type TT_RESB_STD
,WA_RESB_STD type TY_RESB_CDC "like line of LT_RESB_STD
---
move-corresponding <FS_DATA> to WA_RESB_STD.
" already exists in CDC
if WA_RESB_STD eq WA_RESB_CDC. "<FS_RESB_CDC>.
continue. "no change, skip this record
A component name cannot contain a dot in its name, in a Unicode program. Same for any other ABAP symbolic name.
The below code with name .include is not permitted. You were mistaken by DDIC structures, which have different rules.
TYPES: begin of TY_RESB_CDC,
...
UTIME type CDUZEIT,
.include type ZBW_MATERIAL_RESVN_RESB,
end of TY_RESB_CDC.
Instead, you should use the ABAP statement INCLUDE TYPE to include the components of a structure (e.g. ZBW_MATERIAL_RESVN_RESB in your case):
TYPES: begin of TY_RESB_CDC,
...
UTIME type CDUZEIT.
INCLUDE TYPE ZBW_MATERIAL_RESVN_RESB.
TYPES: end of TY_RESB_CDC.

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

Passing a row type to jsonb_to_recordset syntax errors

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;

Add a missing key to JSON in a Postgres table via Rails

I'm trying to use update_all to update any records that is missing a key in a JSON stored in a table cell. ids is the ids of those records and I've tried the below...
User.where(id: ids).
update_all(
"preferences = jsonb_set(preferences, '{some_key}', 'true'"
)
Where the error returns is...
Caused by PG::SyntaxError: ERROR: syntax error at or near "WHERE"
LINE 1: ...onb_set(preferences, '{some_key}', 'true' WHERE "user...
The key takes a string value so not sure why the query is failing.
UPDATE:
Based on what was mentioned, I added the parentheses and also added / modified the last two arguments...
User.where(id: ids).
update_all(
"preferences = jsonb_set(preferences, '{some_key}', 'true'::jsonb, true)"
)
still running into issues and this time it seems related to the key I'm passing
I know this key doesn't currently exist for the set of ids
I added true for create_missing so that 1 isn't an issue
I get this error now...
Caused by PG::UndefinedFunction: ERROR: function jsonb_set(hstore, unknown, jsonb, boolean) does not exis
some_key should be a key in preferences
You're passing in raw SQL so you are 100% responsible for ensuring that is actually valid SQL. What you have there isn't. Check your parentheses:
User.where(id: ids).
update_all(
"preferences = jsonb_set(preferences, '{some_key}', 'true')"
)
If you look more closely at the error message it was telling you there was a problem precisely at the introduction of the WHERE clause, and right after ...true' so that was a good place to look for problems.
Syntax errors like this can be really annoying, but don't forget your database will usually do its best to pin down the place where the problem occurs.

function ts_rank_cd(text, tsquery) does not exist

I'm using full text search supported by postgres, I installed acts_as_tsearch plugin and it works successfully, but when I tried it later I found an error
runtimeError: ERROR C42883 Mfunction ts_rank_cd(text, tsquery) does
not exist HNo function matches the given name and argument types. You
might need to add explicit type
You need to cast your first parameter into a tsvector.
So let's assume you're searching against a column named foo.text. You will want to change from this:
SELECT ts_rank_cd(foo.text, plainto_tsquery('my search terms')) FROM foo;
to this:
SELECT ts_rank_cd(to_tsvector(foo.text), plainto_tsquery('my search terms')) FROM foo;
or something similar.
If you're using the ## operator elsewhere, you can generally re-use the expressions that operator is operating on.
You can find more documentation on to_tsvector at http://www.postgresql.org/docs/current/static/textsearch-controls.html#TEXTSEARCH-PARSING-DOCUMENTS
EDIT
ts_rank_cd(text, tsquery) does not
exist
This means there is no function with this name that accepts text and a tsquery parameter as input. And that's correct, PostgreSQL doens't have a function using these parameters.
From the manual:
ts_rank_cd([ weights float4[], ]
vector tsvector, query tsquery [,
normalization integer ])
Change your input for the function ts_rank_cd() and you will be fine.