#coding:utf-8 """ ID: issue-5450 ISSUE: 5450 TITLE: Allow implicit conversion between boolean and string DESCRIPTION: Test contains TWO set of expressions: those which should finish OK and which should FAIL. Expressions that should work fine are called directly with checking only their result. Expressions that should fail are inserted into table and are called via ES from cursor on that table. Inside this cursor we register values of gdscode and sqlstate that raise, and issue via output args three columns: statement, gdscode, sqlstate. This output is then checked for matching with expected. JIRA: CORE-5167 FBTEST: bugs.core_5167 NOTES: [06.09.2023] pzotov Expression "true in ('unknown', 'false', 'true')" failed in FB 3.x/4.x because first literal ('unknown') could not be conversed properly. This was expected: old algorithm of IN() compared literals using "OR" basis, from left to right, one by one. But this is not so in FB 5.x+ since changed algorithm of IN(), and this expression is evaluated successfully. Because of that, it was decided to replace this expr. with: "select 'unknown' in (true) as result from rdb$database" (it fails on any major FB version). See also: 1) https://github.com/FirebirdSQL/firebird/commit/0493422c9f729e27be0112ab60f77e753fabcb5b ("Better processing and optimization if IN predicates (#7707)") 2) letter from dimitr, 05-sep-2023 20:51 """ import pytest from firebird.qa import * db = db_factory() test_script = """ set list on; recreate sequence g; recreate table test( id int, -- generated by default as identity constraint pk_test primary key using index pk_test, expr varchar(128) ); commit; -- ########################################################### -- ### FOLLOWING STATEMENTS SHOULD FINISH __SUCCESSFULLY__ ### -- ########################################################### -- check trivial ability to convert string to boolean select gen_id(g,1) as expr_no, true > 'false' as result from rdb$database; select gen_id(g,1) as expr_no, 'false' ~= true and true != 'false' and false ^= 'true' as result from rdb$database; -- following should work as: 1) (true > 'false)==> ; 2) true > 'false' ==> select gen_id(g,1) as expr_no, true > 'false' > 'false' as result from rdb$database; -- 1) 'true' > false ==> TRUE > false ==> ; 2) TRUE > 'true' ==> > TRUE ==> select gen_id(g,1) as expr_no, 'true' > false > 'true' as result from rdb$database; select gen_id(g,1) as expr_no, 'true' in (false, false, null, true) as result from rdb$database; -- select gen_id(g,1) as expr_no, 'true' not in (false, null, true) as result from rdb$database; -- select gen_id(g,1) as expr_no, 'true' between false and 'true' as result from rdb$database; -- -- works as: NOT ('true' between false and 'true'): select gen_id(g,1) as expr_no, not 'true' between false and 'true' as result from rdb$database; -- select gen_id(g,1) as expr_no, unknown = 'unknown' as result from rdb$database; -- null select gen_id(g,1) as expr_no, unknown in ('false', 'true','unknown') as result from rdb$database; -- null select gen_id(g,1) as expr_no, unknown = 'false' as result from rdb$database; -- null select gen_id(g,1) as expr_no, unknown > 'true' as result from rdb$database; -- null select gen_id(g,1) as expr_no, unknown between 'false' and 'true' as result from rdb$database; -- null select gen_id(g,1) as expr_no, unknown = 'false' or 'true' = unknown as result from rdb$database; -- null -- check how 'NOT' works (operator precedence) -- Also check 'IS', 'IS DISTINCT FROM' and CaSe InsensItivity of strings: -- ASF, 06/Apr/16 01:37 PM: is 'true' is invalid. -- The operator is: -- IS [NOT] {TRUE | FALSE} -- -- but not like: -- IS [NOT] -- -- not 'false' together with AND/OR, does not allow non boolean argument. select gen_id(g,1) as expr_no, not 'false' = true as result from rdb$database; -- works as: not ( 'false' = true ); result: select gen_id(g,1) as expr_no, not 'faLSe' is distinct from true as result from rdb$database; -- works as: not ( 'false is ... ); result: select gen_id(g,1) as expr_no, not true = 'false' as result from rdb$database; select gen_id(g,1) as expr_no, not true is not distinct from 'false' as result from rdb$database; select gen_id(g,1) as expr_no, 'false' <> not false as result from rdb$database; -- true select gen_id(g,1) as expr_no, 'true' between (not true) and (not false) as result from rdb$database; -- true commit; -- ######################################################### -- ### FOLLOWING STATEMENTS SHOULD FINISH __ABNORMALLY__ ### -- ######################################################### delete from test; -- ASF 11/Apr/16 12:59 AM -- About BETWEEN, if we allow every crazy construct there, parser conflicts explodes. insert into test(id, expr) values(gen_id(g,1), 'select true between not true and not false as result from rdb$database'); -- token unknown ''not'' insert into test(id, expr) values(gen_id(g,1), 'select true between not false and true as result from rdb$database'); -- token unknown ''not'' insert into test(id, expr) values(gen_id(g,1), 'select (not ''true'') as result from rdb$database'); -- invalid usage of bool expr insert into test(id, expr) values(gen_id(g,1), 'select not ''faLSe'' is true as result from rdb$database'); -- works as: not ( ''false'' is true ) -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select ''true'' is not false as result from rdb$database'); -- invalid usage of bool expr insert into test(id, expr) values(gen_id(g,1), 'select ''true'' is distinct from not ''unknown'' as result from rdb$database'); -- invalid usage of bool expr insert into test(id, expr) values(gen_id(g,1), 'select ''true'' is distinct from (not false and ''false'') as result from rdb$database'); -- -Invalid usage of boolean expression insert into test(id, expr) values(gen_id(g,1), 'select ''true'' is not false as result from rdb$database'); -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select true = not ''false'' as result from rdb$database'); -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select ''false'' is not distinct from not true or ''unknown'' as result from rdb$database'); -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select true = not not ''true'' as result from rdb$database'); -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select ''true'' is unknown as result from rdb$database'); -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select ''true'' is unknown as result from rdb$database'); -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select ''true'' is true as result from rdb$database'); -- invalid usage of bool insert into test(id, expr) values(gen_id(g,1), 'select true and ''false'' as result from rdb$database'); -- -Invalid usage of boolean expression insert into test(id, expr) values(gen_id(g,1), 'select ''true'' and false as result from rdb$database'); -- -Invalid usage of boolean expression -- convers error: insert into test(id, expr) values(gen_id(g,1), 'select true = ''unknown'' as result from rdb$database'); -- convers error insert into test(id, expr) values(gen_id(g,1), 'select ''unknown'' in (true) as result from rdb$database'); -- convers error insert into test(id, expr) values(gen_id(g,1), 'select cast(''true'' as blob) > false as result from rdb$database'); insert into test(id, expr) values(gen_id(g,1), 'select list(b, '''') > false as result from (select ''true'' as b from rdb$database)'); commit; set list on; set term ^; execute block returns( expr_no int, run_expr type of column test.expr, raised_gds int, raised_sql char(6) ) as declare bool_result boolean; begin for select id, expr from test order by id into expr_no, run_expr do begin raised_gds=null; raised_sql=null; begin execute statement run_expr into bool_result; when any do begin raised_gds=gdscode; raised_sql=sqlstate; end end suspend; end end ^ set term ;^ commit; """ act = isql_act('db', test_script) expected_stdout = """ EXPR_NO 1 RESULT EXPR_NO 2 RESULT EXPR_NO 3 RESULT EXPR_NO 4 RESULT EXPR_NO 5 RESULT EXPR_NO 6 RESULT EXPR_NO 7 RESULT EXPR_NO 8 RESULT EXPR_NO 9 RESULT EXPR_NO 10 RESULT EXPR_NO 11 RESULT EXPR_NO 12 RESULT EXPR_NO 13 RESULT EXPR_NO 14 RESULT EXPR_NO 15 RESULT EXPR_NO 16 RESULT EXPR_NO 17 RESULT EXPR_NO 18 RESULT EXPR_NO 19 RESULT EXPR_NO 20 RESULT EXPR_NO 21 RUN_EXPR select true between not true and not false as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 42000 EXPR_NO 22 RUN_EXPR select true between not false and true as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 42000 EXPR_NO 23 RUN_EXPR select (not 'true') as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 24 RUN_EXPR select not 'faLSe' is true as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 25 RUN_EXPR select 'true' is not false as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 26 RUN_EXPR select 'true' is distinct from not 'unknown' as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 27 RUN_EXPR select 'true' is distinct from (not false and 'false') as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 28 RUN_EXPR select 'true' is not false as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 29 RUN_EXPR select true = not 'false' as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 30 RUN_EXPR select 'false' is not distinct from not true or 'unknown' as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 31 RUN_EXPR select true = not not 'true' as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 32 RUN_EXPR select 'true' is unknown as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 33 RUN_EXPR select 'true' is unknown as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 34 RUN_EXPR select 'true' is true as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 35 RUN_EXPR select true and 'false' as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 36 RUN_EXPR select 'true' and false as result from rdb$database RAISED_GDS 335544569 RAISED_SQL 22000 EXPR_NO 37 RUN_EXPR select true = 'unknown' as result from rdb$database RAISED_GDS 335544334 RAISED_SQL 22018 EXPR_NO 38 RUN_EXPR select 'unknown' in (true) as result from rdb$database RAISED_GDS 335544334 RAISED_SQL 22018 EXPR_NO 39 RUN_EXPR select cast('true' as blob) > false as result from rdb$database RAISED_GDS 335544334 RAISED_SQL 22018 EXPR_NO 40 RUN_EXPR select list(b, '') > false as result from (select 'true' as b from rdb$database) RAISED_GDS 335544334 RAISED_SQL 22018 """ @pytest.mark.version('>=3.0') def test_1(act: Action): act.expected_stdout = expected_stdout act.execute() assert act.clean_stdout == act.clean_expected_stdout