Skip to content

Commit b802478

Browse files
committed
Fix plan matching.
1 parent effb53c commit b802478

File tree

5 files changed

+201
-65
lines changed

5 files changed

+201
-65
lines changed

src/common/classes/MetaString.h

+3
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ class MetaString
140140

141141
const auto validateUnquotedIdentifier = [&](const string& name)
142142
{
143+
if (name.length() > MAX_SQL_IDENTIFIER_LEN)
144+
(Arg::Gds(isc_invalid_name) << str).raise();
145+
143146
bool first = true;
144147

145148
for (const auto c : name)

src/common/classes/QualifiedMetaString.h

+120-43
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#define QUALIFIED_METASTRING_H
2525

2626
#include "../common/classes/MetaString.h"
27+
#include "../common/classes/objects_array.h"
2728
#include "../common/StatusArg.h"
2829
#include <algorithm>
2930
#include <cctype>
@@ -86,7 +87,7 @@ class BaseQualifiedName
8687
}
8788

8889
public:
89-
static BaseQualifiedName<T> parseSchemaObject(const string& str)
90+
static void parseSchemaObjectListNoSep(const string& str, ObjectsArray<BaseQualifiedName<T>>& list)
9091
{
9192
const auto isQuoted = [](const string& name) -> bool
9293
{
@@ -118,6 +119,9 @@ class BaseQualifiedName
118119

119120
const auto validateUnquotedIdentifier = [&](const string& name)
120121
{
122+
if (name.length() > MAX_SQL_IDENTIFIER_LEN)
123+
(Arg::Gds(isc_invalid_name) << str).raise();
124+
121125
bool first = true;
122126

123127
for (const auto c : name)
@@ -139,64 +143,137 @@ class BaseQualifiedName
139143
return true;
140144
};
141145

142-
BaseQualifiedName<T> result;
143-
string schema, object;
144-
145-
// Find the last unquoted dot to determine schema and object
146-
bool inQuotes = false;
147-
auto lastDotPos = string::npos;
146+
size_t i = 0;
148147

149-
for (size_t i = 0; i < str.size(); ++i)
148+
const auto skipSpaces = [&]()
150149
{
151-
if (str[i] == '"')
152-
inQuotes = !inQuotes;
153-
else if (str[i] == '.' && !inQuotes)
154-
lastDotPos = i;
155-
}
150+
while (i < str.size() &&
151+
(str[i] == ' ' || str[i] == '\t' || str[i] == '\f' || str[i] == '\r' || str[i] == '\n'))
152+
{
153+
++i;
154+
}
155+
};
156+
157+
skipSpaces();
156158

157-
if (lastDotPos != string::npos)
159+
do
158160
{
159-
schema = str.substr(0, lastDotPos);
160-
object = str.substr(lastDotPos + 1);
161-
}
162-
else
163-
object = str;
161+
BaseQualifiedName<T> result;
162+
string schema, object;
164163

165-
schema.trim(" \t\f\r\n");
166-
object.trim(" \t\f\r\n");
164+
const auto start = i;
165+
bool nameFound = false;
166+
bool inQuotes = false;
167+
auto dotPos = string::npos;
167168

168-
// Process schema if it exists
169-
if (schema.hasData())
170-
{
171-
if (isQuoted(schema))
172-
result.schema = unquote(schema);
169+
for (; !nameFound && i < str.size(); ++i)
170+
{
171+
const auto ch = str[i];
172+
173+
if (inQuotes)
174+
{
175+
if (ch == '"')
176+
{
177+
if (i + 1 < str.size() && str[i + 1] == '"')
178+
++i;
179+
else
180+
inQuotes = false;
181+
}
182+
}
183+
else
184+
{
185+
switch (ch)
186+
{
187+
case '"':
188+
inQuotes = true;
189+
break;
190+
191+
case '.':
192+
{
193+
dotPos = i++;
194+
skipSpaces();
195+
--i;
196+
break;
197+
}
198+
199+
case ' ':
200+
case '\t':
201+
case '\f':
202+
case '\r':
203+
case '\n':
204+
{
205+
skipSpaces();
206+
207+
if (i < str.size() && str[i] == '.')
208+
{
209+
dotPos = i++;
210+
skipSpaces();
211+
}
212+
else
213+
nameFound = true;
214+
215+
--i;
216+
break;
217+
}
218+
}
219+
}
220+
}
221+
222+
if (dotPos != string::npos)
223+
{
224+
schema = str.substr(start, dotPos - start);
225+
object = str.substr(dotPos + 1, i - dotPos - 1);
226+
}
173227
else
228+
object = str.substr(start, i - start);
229+
230+
schema.trim(" \t\f\r\n");
231+
object.trim(" \t\f\r\n");
232+
233+
// Process schema if it exists
234+
if (schema.hasData())
174235
{
175-
validateUnquotedIdentifier(schema);
236+
if (isQuoted(schema))
237+
result.schema = unquote(schema);
238+
else
239+
{
240+
validateUnquotedIdentifier(schema);
176241

177-
std::transform(schema.begin(), schema.end(), schema.begin(), ::toupper);
178-
result.schema = schema;
242+
std::transform(schema.begin(), schema.end(), schema.begin(), ::toupper);
243+
result.schema = schema;
244+
}
179245
}
180-
}
181246

182-
if (lastDotPos != string::npos && result.schema.isEmpty())
183-
(Arg::Gds(isc_invalid_name) << str).raise();
247+
if (dotPos != string::npos && result.schema.isEmpty())
248+
(Arg::Gds(isc_invalid_name) << str).raise();
184249

185-
// Process object
186-
if (isQuoted(object))
187-
result.object = unquote(object);
188-
else
189-
{
190-
validateUnquotedIdentifier(object);
250+
// Process object
251+
if (isQuoted(object))
252+
result.object = unquote(object);
253+
else
254+
{
255+
validateUnquotedIdentifier(object);
256+
257+
std::transform(object.begin(), object.end(), object.begin(), ::toupper);
258+
result.object = object;
259+
}
260+
261+
if (result.object.isEmpty())
262+
(Arg::Gds(isc_invalid_name) << str).raise();
191263

192-
std::transform(object.begin(), object.end(), object.begin(), ::toupper);
193-
result.object = object;
194-
}
264+
list.add(result);
265+
} while (i < str.size());
266+
}
267+
268+
static BaseQualifiedName<T> parseSchemaObject(const string& str)
269+
{
270+
ObjectsArray<BaseQualifiedName<T>> list;
271+
parseSchemaObjectListNoSep(str, list);
195272

196-
if (result.object.isEmpty())
273+
if (list.getCount() != 1)
197274
(Arg::Gds(isc_invalid_name) << str).raise();
198275

199-
return result;
276+
return list[0];
200277
}
201278

202279
public:

src/common/classes/tests/MetaStringTest.cpp

-1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,5 @@ BOOST_DATA_TEST_CASE(ParseErrorTest, parseErrorTestData, input)
6969
}
7070

7171

72-
7372
BOOST_AUTO_TEST_SUITE_END() // MetaStringTests
7473
BOOST_AUTO_TEST_SUITE_END() // MetaStringSuite

src/common/classes/tests/QualifiedMetaStringTest.cpp

+40-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,43 @@ BOOST_AUTO_TEST_SUITE(QualifiedMetaStringSuite)
1111
BOOST_AUTO_TEST_SUITE(QualifiedMetaStringTests)
1212

1313

14-
const auto parseTestData = boost::unit_test::data::make({
14+
const auto parseSchemaObjectListNoSepTestData = boost::unit_test::data::make({
15+
make_tuple("Object1", "\"OBJECT1\""),
16+
make_tuple(" Object2 ", "\"OBJECT2\""),
17+
make_tuple(" Object3 Object4 ", "\"OBJECT3\", \"OBJECT4\""),
18+
make_tuple(" \"Object5 \" \" Object6 \" ", "\"Object5\", \" Object6\""),
19+
make_tuple(" Object7 \" Object8 \" ", "\"OBJECT7\", \" Object8\""),
20+
make_tuple(" Object9 Object10 ", "\"OBJECT9\", \"OBJECT10\""),
21+
make_tuple("Schema1 . Obj1 Schema2.Obj2 Obj3 ", "\"SCHEMA1\".\"OBJ1\", \"SCHEMA2\".\"OBJ2\", \"OBJ3\""),
22+
});
23+
24+
BOOST_DATA_TEST_CASE(ParseSchemaObjectListNoSepTest, parseSchemaObjectListNoSepTestData, input, formattedList)
25+
{
26+
const auto parseAndFormatList = [](const char* input) -> std::string
27+
{
28+
ObjectsArray<QualifiedMetaString> list;
29+
QualifiedMetaString::parseSchemaObjectListNoSep(input, list);
30+
31+
if (list.hasData())
32+
{
33+
return std::accumulate(
34+
std::next(list.begin()),
35+
list.end(),
36+
std::string(list[0].toQuotedString().c_str()),
37+
[](const auto& a, const auto& b) {
38+
return a + ", " + (b.object.hasData() ? b.toQuotedString().c_str() : "\" \"");
39+
}
40+
);
41+
}
42+
else
43+
return {};
44+
};
45+
46+
BOOST_TEST(parseAndFormatList(input) == formattedList);
47+
}
48+
49+
50+
const auto parseSchemaObjectTestData = boost::unit_test::data::make({
1551
make_tuple("Object", "", "OBJECT"),
1652
make_tuple("Schema$_0.{Object}", "SCHEMA$_0", "{OBJECT}"),
1753
make_tuple("Schema.Object", "SCHEMA", "OBJECT"),
@@ -25,15 +61,15 @@ const auto parseTestData = boost::unit_test::data::make({
2561
make_tuple(" \"Sch\"\"ma\" . \"Obj\"\"ect\" ", "Sch\"ma", "Obj\"ect"),
2662
});
2763

28-
BOOST_DATA_TEST_CASE(ParseTest, parseTestData, input, expectedSchema, expectedObject)
64+
BOOST_DATA_TEST_CASE(ParseSchemaObjectTest, parseSchemaObjectTestData, input, expectedSchema, expectedObject)
2965
{
3066
const auto name = QualifiedMetaString::parseSchemaObject(input);
3167
BOOST_TEST(name.schema == MetaString(expectedSchema));
3268
BOOST_TEST(name.object == MetaString(expectedObject));
3369
}
3470

3571

36-
const auto parseErrorTestData = boost::unit_test::data::make({
72+
const auto parseSchemaObjectErrorTestData = boost::unit_test::data::make({
3773
"1Object",
3874
"_Object",
3975
"$Object",
@@ -53,12 +89,11 @@ const auto parseErrorTestData = boost::unit_test::data::make({
5389
"\" \" . \" \"",
5490
});
5591

56-
BOOST_DATA_TEST_CASE(ParseErrorTest, parseErrorTestData, input)
92+
BOOST_DATA_TEST_CASE(ParseSchemaObjectErrorTest, parseSchemaObjectErrorTestData, input)
5793
{
5894
BOOST_CHECK_THROW(QualifiedMetaString::parseSchemaObject(input), status_exception);
5995
}
6096

6197

62-
6398
BOOST_AUTO_TEST_SUITE_END() // QualifiedMetaStringTests
6499
BOOST_AUTO_TEST_SUITE_END() // QualifiedMetaStringSuite

src/jrd/RecordSourceNodes.cpp

+38-16
Original file line numberDiff line numberDiff line change
@@ -3432,6 +3432,9 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
34323432

34333433
fb_assert(planRelation || planProcedure);
34343434

3435+
ObjectsArray<QualifiedMetaString> planAliasList;
3436+
QualifiedMetaString::parseSchemaObjectListNoSep(planAlias, planAliasList);
3437+
34353438
const auto name = planRelation ? planRelation->rel_name :
34363439
planProcedure ? planProcedure->getName() :
34373440
QualifiedName();
@@ -3453,17 +3456,44 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
34533456

34543457
auto tailAlias = tail->csb_alias ? *tail->csb_alias : "";
34553458

3456-
if (planAlias.hasData())
3459+
const auto matchAlias = [&]() -> bool
34573460
{
3458-
const auto spacePos = planAlias.find_first_of(' ');
3459-
const auto subAlias = planAlias.substr(0, spacePos); // FIXME:
3461+
if (planAliasList.front() == tailName ||
3462+
(planAliasList.front().schema.isEmpty() && planAliasList.front().object == tailName.object))
3463+
{
3464+
planAliasList.remove(0);
3465+
return true;
3466+
}
34603467

3461-
if (tailName.object == subAlias || tailAlias == subAlias) // FIXME:
3468+
if (tailAlias.hasData())
34623469
{
3463-
planAlias = planAlias.substr(spacePos);
3464-
planAlias.ltrim();
3470+
ObjectsArray<QualifiedMetaString> tailAliasList;
3471+
QualifiedMetaString::parseSchemaObjectListNoSep(tailAlias, tailAliasList);
3472+
3473+
if (planAliasList.getCount() == tailAliasList.getCount())
3474+
{
3475+
auto planIt = planAliasList.begin();
3476+
auto tailIt = tailAliasList.begin();
3477+
3478+
for (; planIt != planAliasList.end(); ++planIt, ++tailIt)
3479+
{
3480+
if (*planIt != *tailIt &&
3481+
!(planIt->schema.isEmpty() && planIt->object == tailIt->object))
3482+
{
3483+
return false;
3484+
}
3485+
}
3486+
3487+
planAliasList.remove(0);
3488+
return true;
3489+
}
34653490
}
3466-
}
3491+
3492+
return false;
3493+
};
3494+
3495+
if (planAlias.hasData())
3496+
matchAlias();
34673497

34683498
// Loop through potentially a stack of views to find the appropriate base table
34693499
StreamType* mapBase;
@@ -3560,16 +3590,8 @@ void RseNode::planSet(CompilerScratch* csb, PlanNode* plan)
35603590

35613591
tailAlias = tail->csb_alias ? *tail->csb_alias : "";
35623592

3563-
const auto spacePos = planAlias.find_first_of(' ');
3564-
const auto subAlias = planAlias.substr(0, spacePos); // FIXME:
3565-
3566-
if (tailName.object == subAlias || tailAlias == subAlias) // FIXME:
3567-
{
3568-
// Skip past the alias
3569-
planAlias = planAlias.substr(spacePos);
3570-
planAlias.ltrim();
3593+
if (matchAlias())
35713594
break;
3572-
}
35733595
}
35743596

35753597
if (!*map)

0 commit comments

Comments
 (0)