PostgreSQL ERROR: function to_tsvector(character varying, unknown) does not exist - sql

This psql session snippet should be self-explanatory:
psql (9.1.7)
Type "help" for help.
=> CREATE TABLE languages(language VARCHAR NOT NULL);
CREATE TABLE
=> INSERT INTO languages VALUES ('english'),('french'),('turkish');
INSERT 0 3
=> SELECT language, to_tsvector('english', 'hello world') FROM languages;
language| to_tsvector
---------+---------------------
english | 'hello':1 'world':2
french | 'hello':1 'world':2
turkish | 'hello':1 'world':2
(3 rows)
=> SELECT language, to_tsvector(language, 'hello world') FROM languages;
ERROR: function to_tsvector(character varying, unknown) does not exist
LINE 1: select language, to_tsvector(language, 'hello world')...
^
HINT: No function matches the given name and argument types.
You might need to add explicit type casts.
The problem is that Postgres function to_tsvector doesn't like varchar field type but this call should be perfectly correct according to the documentation?

Use an explicit type cast:
SELECT language, to_tsvector(language::regconfig, 'hello world') FROM languages;
Or change the column languages.language to type regconfig. See #Swav's answer.
Why?
Postgres allows function overloading. Function signatures are defined by their (optionally schema-qualified) name plus (the list of) input parameter type(s). The 2-parameter form of to_tsvector() expects type regconfig as first parameter:
SELECT proname, pg_get_function_arguments(oid)
FROM pg_catalog.pg_proc
WHERE proname = 'to_tsvector'
proname | pg_get_function_arguments
-------------+---------------------------
to_tsvector | text
to_tsvector | regconfig, text -- you are here
If no existing function matches exactly, the rules of Function Type Resolution decide the best match - if any. This is successful for to_tsvector('english', 'hello world'), with 'english' being an untyped string literal. But fails with a parameter typed varchar, because there is no registered implicit cast from varchar to regconfig. The manual:
Discard candidate functions for which the input types do not match and
cannot be converted (using an implicit conversion) to match. unknown
literals are assumed to be convertible to anything for this purpose.
Bold emphasis mine.
The registered casts for regconfig:
SELECT castsource::regtype, casttarget::regtype, castcontext
FROM pg_catalog.pg_cast
WHERE casttarget = 'regconfig'::regtype;
castsource | casttarget | castcontext
------------+------------+-------------
oid | regconfig | i
bigint | regconfig | i
smallint | regconfig | i
integer | regconfig | i
Explanation for castcontext:
castcontext char
Indicates what contexts the cast can be invoked
in. e means only as an explicit cast (using CAST or :: syntax). a
means implicitly in assignment to a target column, as well as
explicitly. i means implicitly in expressions, as well as the other cases.
Read more about the three different types of assignment in the chapter "CREATE CAST".

Alternative approach to Erwin Brandstetter's answer
You could define your language column to be of type regconfig which would make your query a bit less verbose i.e.:
CREATE TABLE languages(language regconfig NOT NULL DEFAULT 'english'::regconfig)
I have set english as default above, but that's not required. Afterwards your original query
SELECT language, to_tsvector(language, 'hello world') FROM languages;
would work just fine.

Related

Semantic difference between standard SQL and Teradata cast syntax

I'm trying to understand the difference between the following two syntaxes, which appear to be doing the same thing when casting strings to integer:
SELECT CAST('1' AS INTEGER), '1' (INTEGER)
Resulting in:
|'1'|'1'|
|---|---|
|1 |1 |
But they don't do the same thing when chaining the conversion:
SELECT CAST(CAST('1' AS INTEGER) AS VARCHAR(3)), ('1' (INTEGER)) (VARCHAR(3))
I'm now getting:
|'1'|'1'|
|---|---|
|1 | |
The second column contains an empty string, not null. Is there a semantic difference between the two syntaxes, or is this just a bug?
I'm using version 16.20.05.01
As mentioned in the comments (no one wanted to answer?), there's documented difference
of behaviour in the section "How CAST Differs from Teradata Conversion Syntax"
Specifically:
Using Teradata conversion syntax (that is, not using CAST) for explicit conversion of numeric -to-character data requires caution.
The process is as follows:
Convert the numeric value to a character string using the default or specified FORMAT for the numeric value.
Leading and trailing pad characters are not trimmed.
Extend to the right with pad characters if required, or truncate from the right if required, to conform to the target length specification.
If non-pad characters are truncated, no string truncation error is reported.

Express a ternary SQL data type

Context
I just met a single table in a PostgreSQL database which is actually only defining a triplet of coded values that are used across the whole database as a ternary data type. I am a bit astonished at first glance, I feel it's weird; there should be some ternary data type?
I've searched the web, especially the PostgreSQL documentation without any apparent success (I'm probably wrong with my search keywords?!), but maybe there is no other solution.
Question
I would like to know if it exists a ternary (as comparison with binary or boolean) data type in PostgreSQL or more generally in SQL which permits to express a "ternary state" (or "ternary boolean" which is clearly is an abuse of language), which I would represent as a general idea as:
+-------+----------+--------------------+
| id | type | also expressed as |
+-------+----------+--------------------+
| 0 | false | 0 |
| 1 | true | 1 |
| 2 | unknown | 2 |
+-------+----------+--------------------+
where unknown can be whatever third state you are actually dealing with.
I would like to know if it exists a ternary (as comparison with binary or boolean) data type
Actually, the boolean data type is ternary because it can have the values true, false and null.
Consider this table:
create table data (some_number int, some_flag boolean);
And the following data:
insert into data (some_number, some_flag)
values (1, true), (2, false), (3, null);
Then the following:
select *
from data
where some_flag = false;
will only return one row (with some_number = 2)
there is not a specific ternary operator but you could use case
select case when operator =0 then 'false'
when operatore =1 then 'true'
when operator = 2 then 'unknow'
else 'not managed'
end
from your_table
I second a_horse_with_no_name's solution for your specific example, but the more general approach is to use an enum data type:
CREATE TYPE ternary AS ENUM (
'never',
'sometimes',
'always'
);
Constants of such a data type are written as string constantls, e.g. 'never', but the internal storage uses 4 bytes per value, regardless of the length of the label.

How to query a json column for empty objects?

Looking to find all rows where a certain json column contains an empty object, {}. This is possible with JSON arrays, or if I am looking for a specific key in the object. But I just want to know if the object is empty. Can't seem to find an operator that will do this.
dev=# \d test
Table "public.test"
Column | Type | Modifiers
--------+------+-----------
foo | json |
dev=# select * from test;
foo
---------
{"a":1}
{"b":1}
{}
(3 rows)
dev=# select * from test where foo != '{}';
ERROR: operator does not exist: json <> unknown
LINE 1: select * from test where foo != '{}';
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
dev=# select * from test where foo != to_json('{}'::text);
ERROR: operator does not exist: json <> json
LINE 1: select * from test where foo != to_json('{}'::text);
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
dwv=# select * from test where foo != '{}'::json;
ERROR: operator does not exist: json <> json
LINE 1: select * from test where foo != '{}'::json;
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
There is no equality (or inequality) operator for the data type json as a whole, because equality is hard to establish. Consider jsonb in Postgres 9.4 or later, where this is possible. More details in this related answer on dba.SE (last chapter):
How to remove known elements from a JSON[] array in PostgreSQL?
SELECT DISTINCT json_column ... or ... GROUP BY json_column fail for the same reason (no equality operator).
Casting both sides of the expression to text allows = or <> operators, but that's not normally reliable as there are many possible text representations for the same JSON value. In Postgres 9.4 or later, cast to jsonb instead. (Or use jsonb to begin with.)
However, for this particular case (empty object) it works just fine:
select * from test where foo::text <> '{}'::text;
Empty JSON array [] could also be relevant.
Then this could work for both [] and {}:
select * from test where length(foo::text) > 2 ;
You have to be careful. Casting all your data as a different type so you can compare it will have performance issues on a large database.
If your data has a consistent key then you can look for the existence of the key. For example if plan data is {} or {id: '1'}
then you can look for items without 'id'
SELECT * FROM public."user"
where NOT(plan ? 'id')
As of PostgreSQL 9.5 this type of query with JSON data is not possible. On the other hand, I agree it would be very useful and created a request for it:
https://postgresql.uservoice.com/forums/21853-general/suggestions/12305481-check-if-json-is-empty
Feel free to vote it, and hopefully it will be implemented!
In 9.3 it is possible to count the pairs in each object and filter the ones with none
create table test (foo json);
insert into test (foo) values
('{"a":1, "c":2}'), ('{"b":1}'), ('{}');
select *
from test
where (select count(*) from json_each(foo) s) = 0;
foo
-----
{}
or test the existence, probably faster for big objects
select *
from test
where not exists (select 1 from json_each(foo) s);
Both techniques will work flawlessly regardless of formating
According to the JSON Functions and Operators documentation you can use the double arrow function (->>) to get a json object or array field as text. Then do an equality check against a string.
So this worked for me:
SELECT jsonb_col from my_table
WHERE jsonb_col ->> 'key' = '{}';
Or if it's nested more than one level use the path function (#>>)
SELECT jsonb_col from my_table
WHERE jsonb_col #>> '{key, nestedKey}' = '{}';
Currently supported version as of this writing:
Supported Versions: Current (13) / 12 / 11 / 10 / 9.6

ERROR: operator does not exist: numeric ~* unknown

I need to created domain in PostgreSQL for a price. The price must be NUMERIC(9,2) where 9 is precision and 2 - scale. When trying to create domain getting:
ERROR: operator does not exist: numeric ~* unknown
Hint: No operator matches the given name and argument type(s). You might need to add explicit type casts.
QUERY:
CREATE DOMAIN d_price AS NUMERIC(9, 2) NOT NULL
CONSTRAINT Product_price_can_contain_only_double_precision_value
CHECK(VALUE ~*'^(([[:digit:]])+\.([[:digit:]]){2})$');
You need your numeric value as a string before you can use the string operator, change your VALUE to: CAST(VALUE AS TEXT)
Your CHECK constraint is nonsensical, because it applies after the value has already been converted to NUMERIC by the database engine's number parser.
VALUE ~*'^(([[:digit:]])+\.([[:digit:]]){2})$')
appears to say "one or more leading digits, a period, and exactly two trailing digits". You can't do that check in any useful way once the number has already been parsed. Observe:
regress=> SELECT NUMERIC(18,2) '1', NUMERIC(18,2) '1.12345';
numeric | numeric
---------+---------
1.00 | 1.12
(1 row)
No matter what the input is, if it fits inside the NUMERIC you've specified, it'll be extended to fit. If it doesn't fit the NUMERIC size you've given it'll produce an error before your CHECK constraint ever runs.

Does this behavior exist in all major databases?

mysql> select 0.121='0.121';
+---------------+
| 0.121='0.121' |
+---------------+
| 1 |
+---------------+
Does it hold for other database that number='number' is true?
First of all: most databases are using localized number formats. So turning a number into a string will most probably not always be the same as your hard-coded string.
Then: you will get problems with the sql syntax you use. See my experiments with oracle bellow.
In Oracle you always need a FROM clause (except they changed this in version 10).
select 0.121='0.121' from sys.dual
In Oracle, you can't have an expression like this in the select clause.
You need a case statement:
select case when 0.121 = '0.121' then 1 else 0 end as xy
from sys.dual
Then you get an error that it is no number. To fix this, convert it:
select case when To_Char(0.121) = '0.121' then 1 else 0 end as xy
from sys.dual
this will return 0! Because, on my machine, 0.121 is converted to the string ".121". These are Swiss settings. If I had German settings, it would be ",121" (note the comma).
So to finally answer the question: No.
Even if it does. What does this help you ?
I would never, ever, make this assumption anyway. You always need to convert both operands to the same type so that, at least, you know what you are comparing.
Most of the reputable databases will do an implicit conversion for this type of query. There may be published rules for implicit conversions on a particular system - you'd have to look at the vendor coumentation to find out what implicit conversions are done on your system.
For instance,
here's an official reference from Microsoft for SQL Server 2000, and
here's a blog entry on SQL Server implicit conversions.
No.
I don't know why Stackoverflow requires me to enter more than 3 characters in answer to this question.
Postgresql is a little more strict than mysql about type conversion, and does not let you implicitly cast/convert between numbers and strings. This is sane behaviour, and it is getting slightly more strict with newer versions. Some examples, from Postgres 8.4:
db=# select 0.112::float = '0.112'::text;
ERROR: operator does not exist: double precision = text
LINE 1: select 0.112::float = '0.112'::text;
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
db=# select 0.112 = ('0.1' || '12');
ERROR: operator does not exist: numeric = text
LINE 1: select 0.112 = ('0.1' || '12');
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
db=# select 0.112 = ('0.1' || '12')::float; -- explicit cast
t
However, this example (the original question) works:
db=# select 0.122 = '0.122';
t
This is a little surprising (or misleading), given the above. But it has to do with how the query is parsed: when it sees an (unqualified) '0.122' literal, the parser does not necessarily assumes it is of TEXT type, but assigns instead a preliminary "unknown" type; its final type is deduced later by some heuristics.
Anyway, it's bad practice to rely on this, as mentioned by others.