Why can't NULL be converted to JSON's null in postgreSQL? - json

I can perform
SELECT to_json(1)
SELECT to_json(1.4)
SELECT to_json('this is a nice json text')
SELECT to_json('{"become":"json"}')
SELECT to_json('null')
And it all works properly, however when you perform:
SELECT to_json(NULL::TEXT)
You, in fact get the postgres builtin NULL, like if nothing really happened, when I was expecting the same result as to_json('null') for exaple SELECT to_json(someText)::TEXT FROM ... maybe, you'd expect "input", "stuff", "" and null but instead you'd get "input", "stuff", "" and
My question is, why SELECT to_json(NULL::TEXT) doesn't give you a json null, but instead just a NULL pointer? why was it implemented like that in postgres? some specific reasons?

The to_json function is marked as STRICT, which means, return NULL when any parameter is NULL. I'm not sure if this is intended, maybe it's a PostgreSQL bug.
Update: After discussing this on Postgres' mailing list, this is not a bug, but a feature - the situation is not as simple due to the fact that both languages support NULL, but the behavior of NULL is little bit different between these two languages. It's hard to decide whether an SQL NULL should be immediately transformed to a JSON NULL and lose its SQL behavior immediately. If you need different behavior, you can use an SQL function:
CREATE OR REPLACE FUNCTION to_json2(anyelement)
RETURNS json AS $$
SELECT COALESCE(to_json($1), json 'null')
$$ LANGUAGE sql;

Pavel Stehule's answer is great, and has led me to a simpler solution:
SELECT 'null'::json;

Related

how to check if a column has truthy value using laravel's eloquent

Suppose I have Post model that has is_verified column with smallint datatype, how can I get all records that is verified? One thing to do this is using this:
Post::where('is_verified', true)->get();
The code above will produce the following query:
select * from `posts` where `posts`.`is_verified` = true
... which will get me all verified Post records; in note that is_verified on all existing records is either 0 or 1.
However, after I get myself curious and try to manually change some is_verified's record value from 1 to another truthy number e.g. 2, the above eloquent query didn't work as expected anymore: records with is_verified value of 2 didn't get retrieved.
I tried to execute the sql query directly from HeidiSQL as well, but it was just the same. Then I tried to change the = in the sql query to is, and now it's working as expected i.e. all records with truthy is_verified get retrieved:
select * from `posts` where `posts`.`is_verified` is true
So my questions are:
Does the above behaviour is correct and expected?
How can I execute the last sql query in eloquent? One thing I can think of is where('is_verified', '!=', 0) but that feels weird in terms of readability especially when the query is pretty long and a bit complicated
As I stated before, the is_verified column is a smallint. Does this affects the behaviour? Because this conversation here states that boolean column datatype is typically tinyint, not smallint.
And that's it. Thank you in advance!
It is not the correct way to handle boolean values, you shouldn't save boolean columns as smallint, you can use the explicit boolean column type as described in the documentation.
Once you setup the boolean field correctly the logic you have in place will work. So Post::where('is_verified', true)->get(); will return the expected results.
Yes, the problem is the smallint column type, if you put tinyint it also should work like the boolean column. You can read more about the differences here.
After doing some deeper digging, I would like to write down the things I've found:
I have updated my mysql to the newest version as of now (v8) and boolean datatype defined in migration results in tinyint(1) in the db. This is happening turns out because in mysql bool or boolean are actually just the synonyms of tinyint(1), so that was a totally normal behaviour, not due to lower-version issues.
I found #dz0nika answer that states that smallint and tinyint results in different behaviour in the query to be quite incorrect. The two datatypes simply differ in terms of byte-size while storing integer value.
As of mysql documentation, it is stated that:
A value of zero is considered false. Nonzero values are considered true.
But also that:
However, the values TRUE and FALSE are merely aliases for 1 and 0, respectively.
Meaning that:
select * from `posts` where `posts`.`is_verified` = true;
Is the same as
select * from `posts` where `posts`.`is_verified` = 1;
Thus the query will only get Post records with is_verified value of 1.
To get Post records with truthy is_verified value, wether 1, or 2, or 3, etc; use is instead of = in the query:
select * from `posts` where `posts`.`is_verified` is true;
You can read more about these informations here and here (look for the "boolean" part)
So, how about the eloquent query? How can we get Post with truthy is_verified using eloquent?
I still don't know what's best. But instead of using where('is_verified', '!=', 0) as I stated in my question, I believe it's better to use whereRaw() instead:
Post::whereRaw('posts.is_verified is true')->get();
If you found this information to be quite missing or incorrect, please kindly reply. Your opinion is much appreciated.

Is MySQL's IF() broken?

MySQL (5.7.35) chokes with an error "Error Code: 3158. JSON documents may not contain NULL member names." on a query that can be simplified to look like
SELECT IF(false, JSON_OBJECTAGG(NULL, 'whatever'), 0) as x;
So I have to screen the NULL value to make it work:
SELECT IF(false, JSON_OBJECTAGG(IFNULL(NULL, 0), 'whatever'), 0) as x;
Which feels wrong as the expr2 in IF(false, expr2, expr3) is not supposed be even looked at by the engine, unless I'm missing something fundamental.
So my question is, why does MySQL's IF() evaluate/analyse expressions that are not meant to be executed?
Thanks
EDIT: to clarify, my actual code was
SELECT IF(cl.id IS NULL, NULL, JSON_OBJECTAGG(cl.id, cl.somethingelse))
Of cource it is supposed to be looked at and analysed for errors. What if someone else comes by later and sets the condition to true?
If nobody ever will, then you don't need a IF condition in the first place.

Select NULL as column using JOOQ

I'm trying to select NULL as a column in my query using JOOQ, so basically something like this:
SELECT name, NULL as 'someColumn' FROM someTable;
I need to do this, because the result needs to include someColumn (as part of a data standard), but we do not have this information in our database. This works fine in plain SQL, but I'm struggling to reproduce this using JOOQ.
Does anyone know how to do this in a query of this form?
context.select(
SOMETABLE.NAME,
... // Other columns here
DSL.NULL.as("someColumn") // <-- This doesn't exist
)
You can use an inline value
DSL.inline(null)
Depending on your database dialect or query usage, you may need to add a data type to that value, e.g.
DSL.inline(null, SQLDataType.VARCHAR)

Is there a SQL mode for MySQL allowing "WHERE x = NULL" (meaning "WHERE x IS NULL")?

I know that using column = NULL in an SQL statement´s WHERE clause results in an empty result set.
As this is very inefficient in programming environments, I´m searching for a way to let MySQL interpret column = NULL like column IS NULL, maybe by setting an SQL mode?
You can use <=> instead. I wouldn't though. This is not portable.
(Thanks to Martin Smith who pointed me to this which was a solution to my problem)
You can have a case statement in your WHERE clause to use the correct syntax:
http://www.postgresql.org/docs/9.4/static/functions-conditional.html
SELECT * FROM my_items WHERE
CASE WHEN 1=1 THEN mac = '08:00:2b:01:02:03'
WHEN 1=2 THEN mac IS NULL
END;
Obviously, the first case (WHEN 1=1) will always be called in this example. You can put your own logic in that satisfies your condition.
Well, at least PostgreSQL does have a parameter for that
transform_null_equals = true
When is set to true, the query parser will transform
field = NULL
to
field is NULL
beforehand. Something tells me there's an equivalent parameter hidden in MySQL less documented params.
EDIT as of 2020-07-15
My "not really a solution" might be one of my first answers at SO. I'm somewhat ashamed for answering something about another DBRMS engine instead of what I was asked by the OP.
Anyway, the correct answer would be using the <=> operator as others have said in their answers already. You replace = for that and it will behave as other comparisons:
A = B // zero if different, one if equals, this is treated as a boolean result
A != NULL // always null, therefore not false nor true.
A <=> B // zero if different, one if equals, zero is one of the variables are null, 1 if both are.
This is not a setting and therefore it's behavior would never be session-wise or DDBB wise.

Dynamic Values in 'IN' operator

I have a select statement in which the WHERE clause's IN operator. The query works properly as long as some values are passed to question mark (passed from java program). But when there are no values passed, I get a syntax error.
select
this_.categoryAddressMapId as category1_1_0_,
this_.categoryId as categoryId1_0_,
this_.addressId as addressId1_0_
from
icapcheckmyphotos.category_address_map this_ <br>
where
this_.addressId in (
?
)
When there are no parameters passed, I need null set. Please suggest how to modify the where clause. Thanks in advance
Modify your java program. Your choices are to not run the query if there are no addressIds, or ensure that it passes at least one value. If addressId is an integer field, pass -1 or something like that. Personally, I like the first option but it depends on your requirements.
how about doing sometiong like
where (? is null) OR this_.addressId in ( ? )
you may need to add some special treatment to the first part of the OR if your columns does accept NULLS
//pseudo code
...
WHERE 1
if(!null) {
AND this_.addressId in ('stuff')
}