Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions doc/sql.extensions/README.unlist
Original file line number Diff line number Diff line change
Expand Up @@ -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:
<in predicate> ::=
<value> [NOT] IN <table value function unlist short>

<table value function unlist short> ::=
UNLIST ( <input> [, <separator>] [, <data type conversion>] )

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.


28 changes: 28 additions & 0 deletions src/dsql/parse.y
Original file line number Diff line number Diff line change
Expand Up @@ -7756,8 +7756,36 @@ in_predicate
ComparativeBoolNode::DFLAG_ANSI_ANY, $4);
$$ = newNode<NotBoolNode>(node);
}
| value IN table_value_function_unlist_short
{
$$ = newNode<ComparativeBoolNode>(blr_eql, $1,
ComparativeBoolNode::DFLAG_ANSI_ANY, $3);
}
| value NOT IN table_value_function_unlist_short
{
const auto node = newNode<ComparativeBoolNode>(blr_eql, $1,
ComparativeBoolNode::DFLAG_ANSI_ANY, $4);
$$ = newNode<NotBoolNode>(node);
}
;

%type <exprNode> table_value_function_unlist_short
table_value_function_unlist_short
: table_value_function_unlist
{
const auto unlistNode = nodeAs<UnlistFunctionSourceNode>($1);
unlistNode->alias = UnlistFunctionSourceNode::FUNC_NAME;

const auto rseNode = newNode<RseNode>();
rseNode->dsqlFlags |= RecordSourceNode::DFLAG_BODY_WRAPPER;
rseNode->dsqlFrom = newNode<RecSourceListNode>(unlistNode);

const auto selectNode = newNode<SelectExprNode>();
selectNode->querySpec = rseNode;

$$ = selectNode;
}

%type <boolExprNode> exists_predicate
exists_predicate
: EXISTS '(' select_expr ')'
Expand Down
Loading