Skip to content

Commit fb32088

Browse files
ChudaykinAlexalexey.chudaykin
andauthored
Short syntax for UNLIST with IN operator (#8878)
* Feature implementation * Addition to documentation * Documentation adjustments * The code is consistent with the description. * Type inference based on comparison parameter * Removing development artifacts * Redundant variable has been removed --------- Co-authored-by: alexey.chudaykin <alexey.chudaykin@red-soft.ru>
1 parent bce1d34 commit fb32088

4 files changed

Lines changed: 89 additions & 6 deletions

File tree

doc/sql.extensions/README.unlist

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,45 @@ Unacceptable behavior:
7474
SELECT UNLIST FROM UNLIST('UNLIST,A,S,A') AS A;
7575

7676

77+
Short syntax for UNLIST with IN operator:
78+
UNLIST can be used directly with the IN operator without requiring a full subquery.
79+
Instead of writing IN (SELECT * FROM UNLIST(...) AS U), you can use the shorter syntax IN UNLIST(...).
80+
81+
Syntax:
82+
<in predicate> ::=
83+
<value> [NOT] IN <table value function unlist short>
84+
85+
<table value function unlist short> ::=
86+
UNLIST ( <input> [, <separator>] [, <data type conversion>] )
87+
88+
Examples:
89+
90+
A)
91+
SELECT * FROM EMPLOYEE WHERE EMP_NO IN UNLIST('2,4,5,7,11');
92+
B)
93+
SELECT * FROM EMPLOYEE WHERE JOB_COUNTRY IN UNLIST('France,Italy,England');
94+
C)
95+
SELECT EMP_NO FROM EMPLOYEE WHERE JOB_COUNTRY NOT IN UNLIST('USA');
96+
D)
97+
SELECT * FROM EMPLOYEE WHERE DEPT_NO IN UNLIST('100:110:115:120', ':' RETURNING INT);
98+
E)
99+
SET AUTOTERM;
100+
RECREATE PROCEDURE GET_EMPLOYEES_BY_PHONE_EXT (PHONE VARCHAR(1000))
101+
RETURNS (EMPLOYEE_NAME VARCHAR(100))
102+
AS
103+
BEGIN
104+
FOR
105+
SELECT FIRST_NAME FROM EMPLOYEE
106+
WHERE PHONE_EXT IN UNLIST(:PHONE, ',' RETURNING INT)
107+
INTO :EMPLOYEE_NAME
108+
DO
109+
SUSPEND;
110+
END;
111+
112+
SELECT A.EMPLOYEE_NAME FROM GET_EMPLOYEES_BY_PHONE_EXT('2,3,7') AS A;
113+
114+
Note:
115+
The short syntax automatically provides the correlation name and column name,
116+
so they cannot be specified explicitly when using this form.
77117

78118

src/dsql/parse.y

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7801,6 +7801,38 @@ in_predicate
78017801
ComparativeBoolNode::DFLAG_ANSI_ANY, $4);
78027802
$$ = newNode<NotBoolNode>(node);
78037803
}
7804+
| value IN table_value_function_unlist_short(NOTRIAL($1))
7805+
{
7806+
$$ = newNode<ComparativeBoolNode>(blr_eql, $1,
7807+
ComparativeBoolNode::DFLAG_ANSI_ANY, $3);
7808+
}
7809+
| value NOT IN table_value_function_unlist_short(NOTRIAL($1))
7810+
{
7811+
const auto node = newNode<ComparativeBoolNode>(blr_eql, $1,
7812+
ComparativeBoolNode::DFLAG_ANSI_ANY, $4);
7813+
$$ = newNode<NotBoolNode>(node);
7814+
}
7815+
;
7816+
7817+
%type <exprNode> table_value_function_unlist_short(<valueExprNode>)
7818+
table_value_function_unlist_short($autoTypeFromValue)
7819+
: table_value_function_unlist
7820+
{
7821+
const auto unlistNode = nodeAs<UnlistFunctionSourceNode>($1);
7822+
unlistNode->alias = UnlistFunctionSourceNode::FUNC_NAME;
7823+
7824+
if (unlistNode->dsqlField == nullptr)
7825+
unlistNode->dsqlAutoTypeFromValue = $autoTypeFromValue;
7826+
7827+
const auto rseNode = newNode<RseNode>();
7828+
rseNode->dsqlFlags |= RecordSourceNode::DFLAG_BODY_WRAPPER;
7829+
rseNode->dsqlFrom = newNode<RecSourceListNode>(unlistNode);
7830+
7831+
const auto selectNode = newNode<SelectExprNode>();
7832+
selectNode->querySpec = rseNode;
7833+
7834+
$$ = selectNode;
7835+
}
78047836
;
78057837

78067838
%type <boolExprNode> exists_predicate

src/jrd/RecordSourceNodes.cpp

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4380,14 +4380,22 @@ dsql_fld* UnlistFunctionSourceNode::makeField(DsqlCompilerScratch* dsqlScratch)
43804380
field = newField;
43814381

43824382
dsc desc;
4383-
DsqlDescMaker::fromNode(dsqlScratch, &desc, inputItem);
4384-
auto ttype = desc.getCharSet();
4383+
if (dsqlAutoTypeFromValue)
4384+
{
4385+
dsqlAutoTypeFromValue = Node::doDsqlPass(dsqlScratch, dsqlAutoTypeFromValue, false);
4386+
DsqlDescMaker::fromNode(dsqlScratch, &desc, dsqlAutoTypeFromValue);
4387+
}
4388+
else
4389+
{
4390+
DsqlDescMaker::fromNode(dsqlScratch, &desc, inputItem);
4391+
auto ttype = desc.getCharSet();
43854392

4386-
if (ttype == CS_NONE && !desc.isText() && !desc.isBlob())
4387-
ttype = CS_ASCII;
4393+
if (ttype == CS_NONE && !desc.isText() && !desc.isBlob())
4394+
ttype = CS_ASCII;
43884395

4389-
const auto bytesPerChar = DSqlDataTypeUtil(dsqlScratch).maxBytesPerChar(ttype);
4390-
desc.makeText(bytesPerChar * DEFAULT_UNLIST_TEXT_LENGTH, ttype);
4396+
const auto bytesPerChar = DSqlDataTypeUtil(dsqlScratch).maxBytesPerChar(ttype);
4397+
desc.makeVarying(bytesPerChar * DEFAULT_UNLIST_TEXT_LENGTH, ttype);
4398+
}
43914399
MAKE_field(newField, &desc);
43924400
newField->fld_id = 0;
43934401
}

src/jrd/RecordSourceNodes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,9 @@ class UnlistFunctionSourceNode : public TableValueFunctionSourceNode
10701070
{
10711071
return FUNC_NAME;
10721072
}
1073+
1074+
public:
1075+
NestConst<ValueExprNode> dsqlAutoTypeFromValue;
10731076
};
10741077

10751078
class GenSeriesFunctionSourceNode final : public TableValueFunctionSourceNode

0 commit comments

Comments
 (0)