From 269aee48bb0b6a68b6ae1a190f2339e7f5cadb5e Mon Sep 17 00:00:00 2001 From: ChudaykinAlex Date: Tue, 3 Feb 2026 12:03:03 +0300 Subject: [PATCH 1/2] Feature implementation --- src/dsql/parse.y | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 140aa27c903..9275d616a8c 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -7756,8 +7756,36 @@ in_predicate ComparativeBoolNode::DFLAG_ANSI_ANY, $4); $$ = newNode(node); } + | value IN table_value_function_unlist_short + { + $$ = newNode(blr_eql, $1, + ComparativeBoolNode::DFLAG_ANSI_ANY, $3); + } + | value NOT IN table_value_function_unlist_short + { + const auto node = newNode(blr_eql, $1, + ComparativeBoolNode::DFLAG_ANSI_ANY, $4); + $$ = newNode(node); + } ; +%type table_value_function_unlist_short +table_value_function_unlist_short +: table_value_function_unlist + { + const auto unlistNode = nodeAs($1); + unlistNode->alias = UnlistFunctionSourceNode::FUNC_NAME; + + const auto rseNode = newNode(); + rseNode->dsqlFlags |= RecordSourceNode::DFLAG_BODY_WRAPPER; + rseNode->dsqlFrom = newNode(unlistNode); + + const auto selectNode = newNode(); + selectNode->querySpec = rseNode; + + $$ = selectNode; + } + %type exists_predicate exists_predicate : EXISTS '(' select_expr ')' From af90afa3e4715ef2ae161fe9225142e81c4e93ca Mon Sep 17 00:00:00 2001 From: ChudaykinAlex Date: Tue, 3 Feb 2026 12:49:49 +0300 Subject: [PATCH 2/2] Addition to documentation --- doc/sql.extensions/README.unlist | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/doc/sql.extensions/README.unlist b/doc/sql.extensions/README.unlist index 7ca4c5fd680..40847b4ca07 100644 --- a/doc/sql.extensions/README.unlist +++ b/doc/sql.extensions/README.unlist @@ -67,5 +67,45 @@ Unacceptable behavior: SELECT UNLIST FROM UNLIST('UNLIST,A,S,A') AS A; +Short syntax for UNLIST with IN operator: + Since Firebird 6.0, UNLIST can be used directly with the IN operator without requiring a full subquery. + Instead of writing IN (SELECT * FROM UNLIST(...) AS U), you can now use the shorter syntax IN UNLIST(...). + +Syntax: + ::= + [NOT] IN + +
::= + UNLIST ( [, ] [, ] ) + +Examples: + +A) + SELECT * FROM EMPLOYEE WHERE EMP_NO IN UNLIST('2,4,5,7,11'); +B) + SELECT * FROM EMPLOYEE WHERE JOB_COUNTRY IN UNLIST('France,Italy,England'); +C) + SELECT EMP_NO FROM EMPLOYEE WHERE JOB_COUNTRY NOT IN UNLIST('USA'); +D) + SELECT * FROM EMPLOYEE WHERE DEPT_NO IN UNLIST('100:110:115:120', ':' RETURNING INT); +E) + SET AUTOTERM; + RECREATE PROCEDURE GET_EMPLOYEES_BY_PHONE_EXT (PHONE VARCHAR(1000)) + RETURNS (EMPLOYEE_NAME VARCHAR(100)) + AS + BEGIN + FOR + SELECT FIRST_NAME FROM EMPLOYEE + WHERE PHONE_EXT IN UNLIST(:PHONE, ',' RETURNING INT) + INTO :EMPLOYEE_NAME + DO + SUSPEND; + END; + + SELECT A.EMPLOYEE_NAME FROM GET_EMPLOYEES_BY_PHONE_EXT('2,3,7') AS A; + +Note: + The short syntax automatically provides the correlation name and column name, + so they cannot be specified explicitly when using this form.