Skip to content

Commit dfcbe9c

Browse files
committed
Code formatting errors have been fixed.
Adjustments have been made to the README. Fixed a bug with the <listagg overflow clause> behavior, now it is silently ignored. The dsqlMatch function has been redesigned. Redesigned behavior with DISTINCT. Multiple elements are now allowed in the ORDER BY. I also added influences on the sorting direction.
1 parent dbf81d6 commit dfcbe9c

File tree

8 files changed

+73
-44
lines changed

8 files changed

+73
-44
lines changed

doc/sql.extensions/README.listagg

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Authors:
1010

1111
Format:
1212
<listagg set function> ::=
13-
LISTAGG <left paren> [ <set quantifier> ] <character value expression> <comma> <listagg separator> [ <listagg overflow clause> ] <right paren> <within group specification>
13+
LISTAGG <left paren> [ <set quantifier> ] <character value expression> <comma> <listagg separator> [ <listagg overflow clause> ] <right paren> [ <within group specification> ]
1414

1515
<listagg separator> ::=
1616
<character string literal>
@@ -36,14 +36,8 @@ Syntax Rules:
3636

3737
There is a <listagg overflow clause> rule in the standard, which is intended to output an error
3838
when the output value overflows. Since the LIST function always returns a BLOB, it was decided
39-
that this rule would be meaningless. It was not implemented and silently ignored if specified.
40-
41-
If DISTINCT is specified for LISTAGG, then ORDER BY <sort specification list> must fully match
42-
<character value expression>
43-
44-
Notes:
45-
If DISTINCT is specified, the presence of WITHIN GROUP must obey the restriction and will not
46-
affect the subsequent code execution.
39+
that this rule would be meaningless. So the OVERFLOW clause is syntactically supported but
40+
silently ignored if specified.
4741

4842
Examples:
4943
CREATE TABLE TEST_T
@@ -68,6 +62,22 @@ SELECT LISTAGG (DISTINCT COL3, ':') FROM TEST_T;
6862
====
6963
A:B
7064

65+
SELECT LISTAGG (DISTINCT COL3, ':') WITHIN GROUP (ORDER BY COL3 ASCENDING) FROM TEST_T;
66+
====
67+
A:B
68+
69+
SELECT LISTAGG (DISTINCT COL3, ':') WITHIN GROUP (ORDER BY COL3 DESCENDING) FROM TEST_T;
70+
====
71+
B:A
72+
73+
SELECT LISTAGG (DISTINCT COL3, ':') WITHIN GROUP (ORDER BY COL3 DESCENDING, COL4, COL5) FROM TEST_T;
74+
====
75+
B:A
76+
77+
SELECT LISTAGG (DISTINCT COL3, ':') WITHIN GROUP (ORDER BY COL4, COL3 DESCENDING, COL5) FROM TEST_T;
78+
====
79+
A:B
80+
7181
SELECT LISTAGG (DISTINCT COL3, ':') WITHIN GROUP (ORDER BY COL2) FROM TEST_T;
7282
====
7383
A:B
@@ -108,7 +118,7 @@ SELECT LISTAGG (COL2, ':') WITHIN GROUP (ORDER BY COL3 ASC, COL4 DESC) FROM TEST
108118
=======
109119
C:A:D:B
110120

111-
SELECT LISTAGG (ALL COL6, ':')FROM TEST_T;
121+
SELECT LISTAGG (ALL COL6, ':') FROM TEST_T;
112122
=======
113123
П:Д:Ж:Й
114124

@@ -120,6 +130,18 @@ SELECT LISTAGG (ALL COL2, ':') WITHIN GROUP (ORDER BY COL6) FROM TEST_T;
120130
=======
121131
B:C:D:A
122132

133+
SELECT LISTAGG (COL4, ':' ON OVERFLOW TRUNCATE '...' WITHOUT COUNT) WITHIN GROUP (ORDER BY COL3 ASC) FROM TEST_T;
134+
=======
135+
J:L:I:K
136+
137+
SELECT LISTAGG (COL4, ':' ON OVERFLOW TRUNCATE '...' WITH COUNT) WITHIN GROUP (ORDER BY COL3 DESC) FROM TEST_T;
138+
======
139+
I:K:J:L
140+
141+
SELECT LISTAGG (DISTINCT COL3, ':' ON OVERFLOW ERROR) WITHIN GROUP (ORDER BY COL3) FROM TEST_T;
142+
===
143+
A:B
144+
123145
INSERT INTO TEST_T values(5, 'E', NULL, NULL, NULL, NULL);
124146
INSERT INTO TEST_T values(6, 'F', 'C', 'N', true, 'К');
125147

@@ -135,10 +157,3 @@ SELECT LISTAGG (ALL COL2, ':') WITHIN GROUP (ORDER BY COL6 NULLS FIRST) FROM TES
135157
===========
136158
E:B:C:D:F:A
137159

138-
SELECT LISTAGG (DISTINCT COL3, ':') WITHIN GROUP (ORDER BY COL2) FROM TEST_T;
139-
========
140-
Statement failed, SQLSTATE = 42000
141-
SQL error code = -104
142-
-Invalid command
143-
-Sort-key of the ORDER BY specification must match the argument list
144-

src/common/ParserTokens.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ PARSER_TOKEN(TOK_ENCRYPT, "ENCRYPT", true)
205205
PARSER_TOKEN(TOK_END, "END", false)
206206
PARSER_TOKEN(TOK_ENGINE, "ENGINE", true)
207207
PARSER_TOKEN(TOK_ENTRY_POINT, "ENTRY_POINT", true)
208+
PARSER_TOKEN(TOK_ERROR, "ERROR", true)
208209
PARSER_TOKEN(TOK_ESCAPE, "ESCAPE", false)
209210
PARSER_TOKEN(TOK_EXCEPTION, "EXCEPTION", true)
210211
PARSER_TOKEN(TOK_EXCESS, "EXCESS", true)
@@ -292,7 +293,7 @@ PARSER_TOKEN(TOK_LIKE, "LIKE", false)
292293
PARSER_TOKEN(TOK_LIMBO, "LIMBO", true)
293294
PARSER_TOKEN(TOK_LINGER, "LINGER", true)
294295
PARSER_TOKEN(TOK_LIST, "LIST", true)
295-
PARSER_TOKEN(TOK_LISTAGG, "LISTAGG", true)
296+
PARSER_TOKEN(TOK_LISTAGG, "LISTAGG", false)
296297
PARSER_TOKEN(TOK_LN, "LN", true)
297298
PARSER_TOKEN(TOK_LATERAL, "LATERAL", false)
298299
PARSER_TOKEN(TOK_LOCAL, "LOCAL", false)
@@ -523,6 +524,7 @@ PARSER_TOKEN(TOK_TRIGGER, "TRIGGER", false)
523524
PARSER_TOKEN(TOK_TRIM, "TRIM", false)
524525
PARSER_TOKEN(TOK_TRUE, "TRUE", false)
525526
PARSER_TOKEN(TOK_TRUNC, "TRUNC", true)
527+
PARSER_TOKEN(TOK_TRUNCATE, "TRUNCATE", false)
526528
PARSER_TOKEN(TOK_TRUSTED, "TRUSTED", true)
527529
PARSER_TOKEN(TOK_TWO_PHASE, "TWO_PHASE", true)
528530
PARSER_TOKEN(TOK_TYPE, "TYPE", true)

src/dsql/AggNodes.cpp

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ bool AggNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other,
308308
// ASF: We compare name address. That should be ok, as we have only one AggInfo instance
309309
// per function.
310310
return aggInfo.blr == o->aggInfo.blr && aggInfo.name == o->aggInfo.name &&
311-
distinct == o->distinct && dialect1 == o->dialect1 && sort == o->sort;;
311+
distinct == o->distinct && dialect1 == o->dialect1;
312312
}
313313

314314
void AggNode::setParameterName(dsql_par* parameter) const
@@ -448,12 +448,14 @@ bool AggNode::aggPass(thread_db* tdbb, Request* request) const
448448
for (auto& nodeOrder : sort->expressions)
449449
{
450450
dsc toDesc = *(descOrder++);
451-
toDesc.dsc_address = data + (IPTR)toDesc.dsc_address;
451+
toDesc.dsc_address = data + (IPTR) toDesc.dsc_address;
452452
if (const auto fromDsc = EVL_expr(tdbb, request, nodeOrder))
453453
{
454454
if (IS_INTL_DATA(fromDsc))
455+
{
455456
INTL_string_to_key(tdbb, INTL_TEXT_TO_INDEX(fromDsc->getTextType()),
456457
fromDsc, &toDesc, INTL_KEY_UNIQUE);
458+
}
457459
else
458460
MOV_move(tdbb, fromDsc, &toDesc);
459461
}
@@ -465,7 +467,7 @@ bool AggNode::aggPass(thread_db* tdbb, Request* request) const
465467
}
466468

467469
dsc toDesc = asb->desc;
468-
toDesc.dsc_address = data + (IPTR)toDesc.dsc_address;
470+
toDesc.dsc_address = data + (IPTR) toDesc.dsc_address;
469471
MOV_move(tdbb, desc, &toDesc);
470472

471473
return true;
@@ -522,7 +524,7 @@ dsc* AggNode::execute(thread_db* tdbb, Request* request) const
522524
if (distinct)
523525
desc.dsc_address = data + (asb->intl ? asb->keyItems[1].getSkdOffset() : 0);
524526
else
525-
desc.dsc_address = data + (IPTR)asb->desc.dsc_address;
527+
desc.dsc_address = data + (IPTR) asb->desc.dsc_address;
526528

527529
aggPass(tdbb, request, &desc);
528530
}
@@ -937,6 +939,20 @@ DmlNode* ListAggNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch*
937939
return node;
938940
}
939941

942+
bool ListAggNode::dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const
943+
{
944+
if (!AggNode::dsqlMatch(dsqlScratch, other, ignoreMapCast))
945+
return false;
946+
947+
const ListAggNode* o = nodeAs<ListAggNode>(other);
948+
fb_assert(o);
949+
950+
if (dsqlOrderClause || o->dsqlOrderClause)
951+
return PASS1_node_match(dsqlScratch, dsqlOrderClause, o->dsqlOrderClause, ignoreMapCast);
952+
953+
return true;
954+
}
955+
940956
void ListAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
941957
{
942958
DsqlDescMaker::fromNode(dsqlScratch, desc, arg);
@@ -950,20 +966,6 @@ void ListAggNode::genBlr(DsqlCompilerScratch* dsqlScratch)
950966
GEN_sort(dsqlScratch, blr_sort, dsqlOrderClause);
951967
}
952968

953-
AggNode* ListAggNode::pass1(thread_db* tdbb, CompilerScratch* csb)
954-
{
955-
if (sort && distinct)
956-
{
957-
ValueExprNode* const sortNode = *sort->expressions.begin();
958-
if (!arg->sameAs(sortNode, false) || sort->expressions.getCount() > 1)
959-
{
960-
ERR_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << Arg::Gds(isc_dsql_command_err)
961-
<< Arg::Gds(isc_distinct_order_by_err));
962-
}
963-
}
964-
return AggNode::pass1(tdbb, csb);
965-
}
966-
967969
bool ListAggNode::setParameterType(DsqlCompilerScratch* dsqlScratch,
968970
std::function<void (dsc*)> makeDesc, bool forceVarChar)
969971
{

src/dsql/AggNodes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,10 +111,11 @@ class ListAggNode final : public AggNode
111111
holder.add(delimiter);
112112
}
113113

114+
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
115+
114116
Firebird::string internalPrint(NodePrinter& printer) const override;
115117
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
116118
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
117-
AggNode* pass1(thread_db* tdbb, CompilerScratch* csb) override;
118119

119120
bool setParameterType(DsqlCompilerScratch* dsqlScratch,
120121
std::function<void (dsc*)> makeDesc, bool forceVarChar) override;

src/dsql/parse.y

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4723,6 +4723,8 @@ keyword_or_column
47234723
| GREATEST
47244724
| LEAST
47254725
| WITHIN
4726+
| LISTAGG
4727+
| TRUNCATE
47264728
;
47274729

47284730
col_opt
@@ -10044,7 +10046,7 @@ non_reserved_word
1004410046
| SEARCH_PATH
1004510047
| SCHEMA
1004610048
| UNLIST
10047-
| LISTAGG
10049+
| ERROR
1004810050
;
1004910051

1005010052
%%

src/include/firebird/impl/msg/jrd.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,4 +998,3 @@ FB_IMPL_MSG(JRD, 995, missing_value_for_format_pattern, -901, "HY", "000", "Cann
998998
FB_IMPL_MSG(JRD, 996, invalid_name, -901, "HY", "000", "Invalid name: @1")
999999
FB_IMPL_MSG(JRD, 997, invalid_unqualified_name_list, -901, "HY", "000", "Invalid list of unqualified names: @1")
10001000
FB_IMPL_MSG(JRD, 998, no_user_att_while_restore, -901, "HY", "000", "User attachments are not allowed for the database being restored")
1001-
FB_IMPL_MSG(JRD, 999, distinct_order_by_err, -208, "42", "000", "Sort-key of the ORDER BY specification must match the argument list")

src/include/gen/Firebird.pas

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5844,7 +5844,6 @@ IProfilerStatsImpl = class(IProfilerStats)
58445844
isc_invalid_name = 335545316;
58455845
isc_invalid_unqualified_name_list = 335545317;
58465846
isc_no_user_att_while_restore = 335545318;
5847-
isc_distinct_order_by_err = 335545319;
58485847
isc_gfix_db_name = 335740929;
58495848
isc_gfix_invalid_sw = 335740930;
58505849
isc_gfix_incmp_sw = 335740932;

src/jrd/optimizer/Optimizer.cpp

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1378,13 +1378,22 @@ void Optimizer::generateAggregateDistincts(MapNode* map)
13781378
sort_key_def* sort_key = asb->keyItems.getBuffer(asb->intl ? 2 : 1);
13791379
sort_key->setSkdOffset();
13801380

1381+
UCHAR direction = SKD_ascending;
1382+
if (aggNode->sort)
1383+
{
1384+
ValueExprNode* const node = aggNode->sort->expressions.front();
1385+
const SortDirection sortDir = aggNode->sort->direction.front();
1386+
if (aggNode->arg->sameAs(node, false) && sortDir == ORDER_DESC)
1387+
direction = SKD_descending;
1388+
}
1389+
13811390
if (asb->intl)
13821391
{
13831392
const USHORT key_length = ROUNDUP(INTL_key_length(tdbb,
13841393
INTL_TEXT_TO_INDEX(desc->getTextType()), desc->getStringLength()), sizeof(SINT64));
13851394

13861395
sort_key->setSkdLength(SKD_bytes, key_length);
1387-
sort_key->skd_flags = SKD_ascending;
1396+
sort_key->skd_flags = direction;
13881397
sort_key->skd_vary_offset = 0;
13891398

13901399
++sort_key;
@@ -1412,7 +1421,7 @@ void Optimizer::generateAggregateDistincts(MapNode* map)
14121421
// see AggNode::aggPass() for details; the length remains rounded properly
14131422
asb->length += sizeof(ULONG);
14141423

1415-
sort_key->skd_flags = SKD_ascending;
1424+
sort_key->skd_flags = direction;
14161425
asb->impure = csb->allocImpure<impure_agg_sort>();
14171426
asb->desc = *desc;
14181427

@@ -1437,8 +1446,8 @@ void Optimizer::generateAggregateSort(AggNode* aggNode)
14371446
const auto keyCount = aggNode->sort->expressions.getCount() * 2;
14381447
sort_key_def* sortKey = asb->keyItems.getBuffer(keyCount);
14391448

1440-
auto const* direction = aggNode->sort->direction.begin();
1441-
auto const* nullOrder = aggNode->sort->nullOrder.begin();
1449+
const auto* direction = aggNode->sort->direction.begin();
1450+
const auto* nullOrder = aggNode->sort->nullOrder.begin();
14421451

14431452
for (auto& node : aggNode->sort->expressions)
14441453
{

0 commit comments

Comments
 (0)