I am having issues casting a jsonb value. And would love some guidance.
what we are trying to achieve is that some data came in as strings, and we want to cast that to numbers.
Consider the following update statement:
update customer
set traits = jsonb_set(traits, '{arr}',traits->'arr'::text::integer)
where jsonb_typeof(traits->'arr') = 'string'
and traits->'arr' is not null
We currently get the following error:
[22P02] ERROR: invalid input syntax for type integer: "arr"
I have tried all sort of casting incantations, but can't figure away past this.
Anyone have a path forward for us ?!
working solution looks like THIS:
update customer
set traits = jsonb_set(traits, '{arr}',(traits->>'arr')::integer::text::jsonb)
where jsonb_typeof(traits->'arr') = 'string'
and traits->'arr' is not null
with a triple cast. Smells a bit off
The problem is that your expression
traits->'arr'::text::integer
is evaluated as
traits->('arr'::text::integer)
which is trying to cast 'arr' to an integer (failing for obvious reasons with the error message you mention). Instead, you want
(traits->'arr')::text::integer
-- or
(traits->>'arr')::integer
Related
Given the following two lines, XCode is showing an error for the second line saying Cannot convert value of type 'String' to expected argument type 'OSLogMessage'.
logger.info("Device found:")
logger.info(String(describing: device))
Can someone please explain why this error is shown? In both cases, the parameter is of type String. (I guess)
Currently, I fix this by using string interpolation. But this does not feel right. Is there a better way than:
logger.info("\(String(describing: device))")
Reading the PostgreSQL docs, I see that you can cast a longish bit of text to xml like this:
SELECT xml '<long>long text, may span many lines</long>'
SELECT xml '...'
Curious, I found that I could do the same with JSON:
SELECT json '{"arg1":"val1", <more args spanning many lines>}'
(I couldn't find an official reference for this one. It just works!)
By contrast, this does not work:
SELECT float8 3.14159
I like this alternate syntax from a readability perspective. Now I'm looking for a reference listing which types may be specified up front like this. but I haven't found it yet.
Any pointers?
The documentation says:
A constant of an arbitrary type can be entered using any one of the following notations:
type 'string'
'string'::type
CAST ( 'string' AS type )
The string constant's text is passed to the input conversion routine for the type called type. The result is a constant of the indicated type. The explicit type cast can be omitted if there is no ambiguity as to the type the constant must be (for example, when it is assigned directly to a table column), in which case it is automatically coerced.
The form you are asking about is the first one.
So this can be used for all PostgreSQL types.
Note that the data must be specified as a string literal (in single or dollar quotes) when you use that syntax.
Trying to parameterize my SQL queries (using libpq function PQexecParams), I was stuck on a syntax error:
SELECT date $1
The error is:
ERROR: syntax error at or near "$1"
Prepared statements
The explanation for this can be found in the chapter Constants of Other Types of the manual:
The ::, CAST(), and function-call syntaxes can also be used to specify
run-time type conversions of arbitrary expressions, as discussed in
Section 4.2.9. To avoid syntactic ambiguity, the type 'string' syntax
can only be used to specify the type of a simple literal constant.
Another restriction on the type 'string' syntax is that it does not
work for array types; use :: or CAST() to specify the type of an array
constant.
Bold emphasis mine.
Parameters for prepared statements are not actually sting literals but typed values, so you cannot use the form type 'string'. Use one of the other two forms to cast the value to a different type, like you found yourself already.
Example:
PREPARE foo AS SELECT $1::date;
EXECUTE foo('2005-1-1');
Similar for PQexecParams in the libpq C library
The documentation:
... In the SQL command text, attach an explicit cast to the parameter
symbol to show what data type you will send. For example:
SELECT * FROM mytable WHERE x = $1::bigint;
This forces parameter $1 to be treated as bigint, whereas by default
it would be assigned the same type as x. Forcing the parameter type
decision, either this way or by specifying a numeric type OID, is
strongly recommended. ...
The alternative, as mentioned in the quote above, is to pass the OIDs of respective data types with paramTypes[] - if you actually need the cast. In most cases it should work just fine to let Postgres derive data types from the query context.
paramTypes[]
Specifies, by OID, the data types to be assigned to the parameter
symbols. If paramTypes is NULL, or any particular element in the array
is zero, the server infers a data type for the parameter symbol in the
same way it would do for an untyped literal string.
You can get the OID of data types from the system catalog pg_type:
SELECT oid FROM pg_type WHERE typname = 'date';
You must use the correct internal type name. For instance: int4 for integer.
Or with a convenience cast to regtype:
SELECT 'date'::regtype::oid;
This is more flexible as known aliases for the type name are accepted as well. For instance: int4, int or integer for integer.
The solution is to use a type cast instead of date:
SELECT $1::date
Consider the following query errors:
db=# select 'test' || 123;
ERROR: operator is not unique: unknown || integer
LINE 1: select 'test' || 123;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
db=# select 'test'::text || 123;
ERROR: operator is not unique: text || integer
LINE 1: select 'test'::text || 123;
^
HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
Now, in PGAdmin, in my Casts node for the DB, I have 13 of them defined, one of which is:
CREATE CAST (integer AS text)
WITH FUNCTION text(integer)
AS IMPLICIT;
When we went to PG 9.1, I recreated implicit casts following the method described here.
I'm wondering if I have indeed created duplicate operators, and if so, how should I go about cleaning it up? If not, why am I getting this kind of error? It seems a rather straight forward cast.
Thanks!
I've tried adding the same cast and it gives me the same error in 9.1.2. I notice also the first comment on that blog (dated Feb 24 2009) reports this error.
This may have something to do with the following comment in the manual:
Note: Before PostgreSQL 8.3, these functions would silently accept values of several non-string data types as well, due to the presence of implicit coercions from those data types to text. Those coercions have been removed because they frequently caused surprising behaviors. However, the string concatenation operator (||) still accepts non-string input, so long as at least one input is of a string type.
I think you are creating a duplicate operator by adding the implicit cast. The integer could be cast to text and use text || text or not and use text || integer. Looking in the tables I think the second may be text || anynonarray which can not be dropped.
In short the only way to fix this is use explicit casts when using the || operator OR drop the implicit cast you've created and use explicit casts every where else.
faced same kind of issue where to solve one error i have to add those old implicit casting but that caused error somewhere else like - SELECT 1|| '/%'; query will throw error of not unique operator, i had to drop integer as text cast.
So, I have this query:
SELECT count(*) AS count FROM table1 INNER JOIN "some query"
WHERE "some more query"
OR (table.smowid NOT LIKE (58)) OR "rest of query"
Sorry for the unclear code, the full query is quite big, but the problem is in the table.smowid NOT LIKE (58) part.
This is the error I get:
ERROR: operator does not exist: integer !~~ integer
LINE 11: or ( (table.smowid) NOT LIKE (58))
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
I unfortunately, don't know much about casts and types, but why is the DB complaining when it's going from integer to integer?
I tried CASTING by saying (CAST (table.smowid) AS INTEGER) NOT LIKE (58) but that didn't work, I also tried (table.smowid :: integer) NOT LIKE (58) but that also didn't work for some reason.
So, what should I do? Thanks for all the help.
you should cast them both to text or, better yet, have a smowis column with all the smowi values as text to save the overhead of casting each time.
NOT LIKE and its equivalent !~~ are definitely not integer operators. What are you trying to do with a pattern matching operators on integers?
There's a way to use it but you first have to cast (or format) the integers to text.