diff --git a/builds/win32/msvc15/isql_static.vcxproj b/builds/win32/msvc15/isql_static.vcxproj
index 2fe7388b96..647eff13af 100644
--- a/builds/win32/msvc15/isql_static.vcxproj
+++ b/builds/win32/msvc15/isql_static.vcxproj
@@ -24,6 +24,7 @@
+
@@ -40,6 +41,7 @@
+
diff --git a/builds/win32/msvc15/isql_static.vcxproj.filters b/builds/win32/msvc15/isql_static.vcxproj.filters
index 5605a4551f..285226069d 100644
--- a/builds/win32/msvc15/isql_static.vcxproj.filters
+++ b/builds/win32/msvc15/isql_static.vcxproj.filters
@@ -30,6 +30,9 @@
ISQL files
+
+ ISQL files
+
ISQL files
@@ -73,6 +76,9 @@
Header files
+
+ Header files
+
Header files
diff --git a/builds/win32/msvc15/isql_test.vcxproj b/builds/win32/msvc15/isql_test.vcxproj
index dabd1312a7..d8912dbe15 100644
--- a/builds/win32/msvc15/isql_test.vcxproj
+++ b/builds/win32/msvc15/isql_test.vcxproj
@@ -177,6 +177,7 @@
+
diff --git a/builds/win32/msvc15/isql_test.vcxproj.filters b/builds/win32/msvc15/isql_test.vcxproj.filters
index 331157ba2b..26868fd4e6 100644
--- a/builds/win32/msvc15/isql_test.vcxproj.filters
+++ b/builds/win32/msvc15/isql_test.vcxproj.filters
@@ -10,6 +10,9 @@
source
+
+ source
+
source
diff --git a/src/common/StdHelper.h b/src/common/StdHelper.h
new file mode 100644
index 0000000000..c055a1257e
--- /dev/null
+++ b/src/common/StdHelper.h
@@ -0,0 +1,42 @@
+/*
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Adriano dos Santos Fernandes
+ * for the Firebird Open Source RDBMS project.
+ *
+ * Copyright (c) 2024 Adriano dos Santos Fernandes
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ *
+ */
+
+#ifndef FB_COMMON_STD_HELPER_H
+#define FB_COMMON_STD_HELPER_H
+
+namespace Firebird {
+
+// To be used with std::visit
+
+template
+struct StdVisitOverloads : Ts...
+{
+ using Ts::operator()...;
+};
+
+template
+StdVisitOverloads(Ts...) -> StdVisitOverloads;
+
+} // namespace Firebird
+
+#endif // FB_COMMON_STD_HELPER_H
diff --git a/src/common/classes/MetaString.h b/src/common/classes/MetaString.h
index 251aedd360..6c954d3c07 100644
--- a/src/common/classes/MetaString.h
+++ b/src/common/classes/MetaString.h
@@ -93,6 +93,30 @@ public:
int compare(const AbstractString& s) const { return compare(s.c_str(), s.length()); }
int compare(const MetaString& m) const { return memcmp(data, m.data, MAX_SQL_IDENTIFIER_SIZE); }
+ string toQuotedString() const
+ {
+ string s;
+
+ if (hasData())
+ {
+ s.reserve(count + 2);
+
+ s.append("\"");
+
+ for (const auto c : *this)
+ {
+ if (c == '"')
+ s.append("\"");
+
+ s.append(&c, 1);
+ }
+
+ s.append("\"");
+ }
+
+ return s;
+ }
+
bool operator==(const char* s) const { return compare(s) == 0; }
bool operator!=(const char* s) const { return compare(s) != 0; }
bool operator==(const AbstractString& s) const { return compare(s) == 0; }
diff --git a/src/isql/FrontendLexer.cpp b/src/isql/FrontendLexer.cpp
index 5c5a63d911..2a3c3602a0 100644
--- a/src/isql/FrontendLexer.cpp
+++ b/src/isql/FrontendLexer.cpp
@@ -28,9 +28,7 @@
#include
-static std::string trim(std::string_view str);
-
-static std::string trim(std::string_view str)
+std::string FrontendLexer::trim(std::string_view str)
{
auto finish = str.end();
auto start = str.begin();
@@ -142,7 +140,7 @@ std::variant= term.length() && std::equal(term.begin(), term.end(), pos))
+ if (std::size_t(end - pos) >= term.length() && std::equal(term.begin(), term.end(), pos))
{
const auto initialStatement = std::string(buffer.cbegin(), pos);
pos += term.length();
@@ -224,13 +222,58 @@ FrontendLexer::Token FrontendLexer::getToken()
return token;
}
-std::optional FrontendLexer::getStringToken()
+FrontendLexer::Token FrontendLexer::getNameToken()
{
+ skipSpacesAndComments();
+
Token token;
+ if (pos >= end)
+ {
+ token.type = Token::TYPE_EOF;
+ return token;
+ }
+
+ if (const auto optStringToken = getStringToken(); optStringToken.has_value())
+ return optStringToken.value();
+
+ const auto start = pos;
+ bool first = true;
+
+ while (pos < end)
+ {
+ const auto c = *pos++;
+
+ if (!((c >= 'A' && c <= 'Z') ||
+ (c >= 'a' && c <= 'z') ||
+ c == '{' ||
+ c == '}' ||
+ (!first && c >= '0' && c <= '9') ||
+ (!first && c == '$') ||
+ (!first && c == '_')))
+ {
+ if (!first)
+ --pos;
+
+ break;
+ }
+
+ first = false;
+ }
+
+ token.processedText = token.rawText = std::string(start, pos);
+ std::transform(token.processedText.begin(), token.processedText.end(),
+ token.processedText.begin(), toupper);
+
+ return token;
+}
+
+std::optional FrontendLexer::getStringToken()
+{
if (pos >= end)
return std::nullopt;
+ Token token;
const auto start = pos;
switch (toupper(*pos))
diff --git a/src/isql/FrontendLexer.h b/src/isql/FrontendLexer.h
index d2ac1f88f2..1357da7a14 100644
--- a/src/isql/FrontendLexer.h
+++ b/src/isql/FrontendLexer.h
@@ -48,6 +48,12 @@ public:
Type type = TYPE_OTHER;
std::string rawText;
std::string processedText;
+
+ std::string getProcessedString() const
+ {
+ return type == FrontendLexer::Token::TYPE_STRING || type == FrontendLexer::Token::TYPE_META_STRING ?
+ processedText : rawText;
+ }
};
struct SingleStatement
@@ -74,6 +80,7 @@ public:
FrontendLexer& operator=(const FrontendLexer&) = delete;
public:
+ static std::string trim(std::string_view str);
static std::string stripComments(std::string_view statement);
public:
@@ -87,6 +94,11 @@ public:
return pos;
}
+ void setPos(std::string::const_iterator newPos)
+ {
+ pos = newPos;
+ }
+
void rewind()
{
deletePos = buffer.begin();
@@ -97,7 +109,9 @@ public:
void appendBuffer(std::string_view newBuffer);
void reset();
std::variant getSingleStatement(std::string_view term);
+
Token getToken();
+ Token getNameToken();
private:
std::optional getStringToken();
diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp
new file mode 100644
index 0000000000..46735e7350
--- /dev/null
+++ b/src/isql/FrontendParser.cpp
@@ -0,0 +1,732 @@
+/*
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Adriano dos Santos Fernandes
+ * for the Firebird Open Source RDBMS project.
+ *
+ * Copyright (c) 2024 Adriano dos Santos Fernandes
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ *
+ */
+
+#include "firebird.h"
+#include "../isql/FrontendParser.h"
+#include
+
+
+FrontendParser::AnyNode FrontendParser::internalParse()
+{
+ static constexpr std::string_view TOKEN_ADD("ADD");
+ static constexpr std::string_view TOKEN_BLOBDUMP("BLOBDUMP");
+ static constexpr std::string_view TOKEN_BLOBVIEW("BLOBVIEW");
+ static constexpr std::string_view TOKEN_CONNECT("CONNECT");
+ static constexpr std::string_view TOKEN_COPY("COPY");
+ static constexpr std::string_view TOKEN_CREATE("CREATE");
+ static constexpr std::string_view TOKEN_DROP("DROP");
+ static constexpr std::string_view TOKEN_EDIT("EDIT");
+ static constexpr std::string_view TOKEN_EXIT("EXIT");
+ static constexpr std::string_view TOKEN_EXPLAIN("EXPLAIN");
+ static constexpr std::string_view TOKEN_HELP("HELP");
+ static constexpr std::string_view TOKEN_INPUT("INPUT");
+ static constexpr std::string_view TOKEN_OUTPUT("OUTPUT");
+ static constexpr std::string_view TOKEN_QUIT("QUIT");
+ static constexpr std::string_view TOKEN_SET("SET");
+ static constexpr std::string_view TOKEN_SHELL("SHELL");
+ static constexpr std::string_view TOKEN_SHOW("SHOW");
+
+ const auto commandToken = lexer.getToken();
+
+ if (commandToken.type == Token::TYPE_OTHER)
+ {
+ const auto& command = commandToken.processedText;
+
+ if (command == TOKEN_ADD)
+ {
+ if (const auto tableName = parseName())
+ {
+ AddNode node;
+ node.tableName = std::move(tableName.value());
+
+ if (parseEof())
+ return node;
+ }
+ }
+ else if (command == TOKEN_BLOBDUMP || command == TOKEN_BLOBVIEW)
+ {
+ if (const auto blobId = lexer.getToken(); blobId.type != Token::TYPE_EOF)
+ {
+ BlobDumpViewNode node;
+
+ // Find the high and low values of the blob id
+ if (blobId.processedText.empty())
+ return InvalidNode();
+
+ sscanf(blobId.processedText.c_str(), "%" xLONGFORMAT":%" xLONGFORMAT,
+ &node.blobId.gds_quad_high, &node.blobId.gds_quad_low);
+
+ if (command == TOKEN_BLOBDUMP)
+ {
+ if (const auto file = parseFileName())
+ {
+ node.file = std::move(file.value());
+
+ if (parseEof())
+ return node;
+ }
+ }
+ else
+ {
+ if (parseEof())
+ return node;
+ }
+ }
+ }
+ else if (command == TOKEN_CONNECT)
+ {
+ ConnectNode node;
+
+ do
+ {
+ const auto token = lexer.getToken();
+
+ if (token.type == Token::TYPE_EOF)
+ {
+ if (node.args.empty())
+ break;
+ else
+ return node;
+ }
+ else if (token.type != Token::TYPE_OTHER &&
+ token.type != Token::TYPE_STRING &&
+ token.type != Token::TYPE_META_STRING)
+ {
+ return InvalidNode();
+ }
+
+ node.args.push_back(std::move(token));
+ } while(true);
+ }
+ else if (command == TOKEN_COPY)
+ {
+ CopyNode node;
+
+ if (const auto source = parseName())
+ node.source = std::move(source.value());
+ else
+ return InvalidNode();
+
+ if (const auto destination = parseName())
+ node.destination = std::move(destination.value());
+ else
+ return InvalidNode();
+
+ if (const auto database = parseFileName())
+ node.database = std::move(database.value());
+ else
+ return InvalidNode();
+
+ if (parseEof())
+ return node;
+ }
+ else if (command == TOKEN_CREATE)
+ {
+ if (const auto createWhat = lexer.getToken();
+ createWhat.type == Token::TYPE_OTHER &&
+ (createWhat.processedText == "DATABASE" ||
+ (options.schemaAsDatabase && createWhat.processedText == "SCHEMA")))
+ {
+ CreateDatabaseNode node;
+
+ do
+ {
+ const auto token = lexer.getToken();
+
+ if (token.type == Token::TYPE_EOF)
+ {
+ if (node.args.empty())
+ break;
+ else
+ return node;
+ }
+ else if (token.type != Token::TYPE_OTHER &&
+ token.type != Token::TYPE_STRING &&
+ token.type != Token::TYPE_META_STRING)
+ {
+ return InvalidNode();
+ }
+
+ node.args.push_back(std::move(token));
+ } while(true);
+ }
+ }
+ else if (command == TOKEN_DROP)
+ {
+ if (const auto dropWhat = lexer.getToken();
+ dropWhat.type == Token::TYPE_OTHER &&
+ (dropWhat.processedText == "DATABASE" ||
+ (options.schemaAsDatabase && dropWhat.processedText == "SCHEMA")))
+ {
+ if (parseEof())
+ return DropDatabaseNode();
+ }
+ }
+ else if (command == TOKEN_EDIT)
+ {
+ EditNode node;
+ node.file = parseFileName();
+
+ if (parseEof())
+ return node;
+ }
+ else if (command == TOKEN_EXIT)
+ {
+ if (parseEof())
+ return ExitNode();
+ }
+ else if (command == TOKEN_EXPLAIN)
+ {
+ ExplainNode node;
+
+ if (const auto query = parseUtilEof())
+ {
+ node.query = std::move(query.value());
+ return node;
+ }
+ }
+ else if (command == TOKEN_HELP || command == "?")
+ {
+ HelpNode node;
+
+ if (const auto token = lexer.getToken(); token.type == Token::TYPE_EOF)
+ return node;
+ else if (token.type == Token::TYPE_OTHER)
+ {
+ node.command = token.processedText;
+
+ if (parseEof())
+ return node;
+ }
+ }
+ else if (command.length() >= 2 && TOKEN_INPUT.find(command) == 0)
+ {
+ if (const auto file = parseFileName())
+ {
+ InputNode node;
+ node.file = std::move(file.value());
+
+ if (parseEof())
+ return node;
+ }
+ }
+ else if (command.length() >= 3 && TOKEN_OUTPUT.find(command) == 0)
+ {
+ OutputNode node;
+ node.file = parseFileName();
+
+ if (parseEof())
+ return node;
+ }
+ else if (command == TOKEN_QUIT)
+ {
+ if (parseEof())
+ return QuitNode();
+ }
+ else if (command == TOKEN_SET)
+ {
+ if (const auto setNode = parseSet(); !std::holds_alternative(setNode))
+ return setNode;
+ }
+ else if (command == TOKEN_SHELL)
+ {
+ ShellNode node;
+ node.command = parseUtilEof();
+ return node;
+ }
+ else if (command == TOKEN_SHOW)
+ {
+ if (const auto showNode = parseShow(); !std::holds_alternative(showNode))
+ return showNode;
+ }
+ }
+
+ return InvalidNode();
+}
+
+FrontendParser::AnySetNode FrontendParser::parseSet()
+{
+ static constexpr std::string_view TOKEN_AUTODDL("AUTODDL");
+ static constexpr std::string_view TOKEN_AUTOTERM("AUTOTERM");
+ static constexpr std::string_view TOKEN_BAIL("BAIL");
+ static constexpr std::string_view TOKEN_BLOBDISPLAY("BLOBDISPLAY");
+ static constexpr std::string_view TOKEN_BULK_INSERT("BULK_INSERT");
+ static constexpr std::string_view TOKEN_COUNT("COUNT");
+ static constexpr std::string_view TOKEN_ECHO("ECHO");
+ static constexpr std::string_view TOKEN_EXEC_PATH_DISPLAY("EXEC_PATH_DISPLAY");
+ static constexpr std::string_view TOKEN_EXPLAIN("EXPLAIN");
+ static constexpr std::string_view TOKEN_HEADING("HEADING");
+ static constexpr std::string_view TOKEN_KEEP_TRAN_PARAMS("KEEP_TRAN_PARAMS");
+ static constexpr std::string_view TOKEN_LIST("LIST");
+ static constexpr std::string_view TOKEN_LOCAL_TIMEOUT("LOCAL_TIMEOUT");
+ static constexpr std::string_view TOKEN_MAXROWS("MAXROWS");
+ static constexpr std::string_view TOKEN_NAMES("NAMES");
+ static constexpr std::string_view TOKEN_PER_TABLE_STATS("PER_TABLE_STATS");
+ static constexpr std::string_view TOKEN_PLAN("PLAN");
+ static constexpr std::string_view TOKEN_PLANONLY("PLANONLY");
+ static constexpr std::string_view TOKEN_ROWCOUNT("ROWCOUNT");
+ static constexpr std::string_view TOKEN_SQL("SQL");
+ static constexpr std::string_view TOKEN_SQLDA_DISPLAY("SQLDA_DISPLAY");
+ static constexpr std::string_view TOKEN_STATS("STATS");
+ static constexpr std::string_view TOKEN_TERMINATOR("TERMINATOR");
+ static constexpr std::string_view TOKEN_TIME("TIME");
+ static constexpr std::string_view TOKEN_TRANSACTION("TRANSACTION");
+ static constexpr std::string_view TOKEN_WARNINGS("WARNINGS");
+ static constexpr std::string_view TOKEN_WIDTH("WIDTH");
+ static constexpr std::string_view TOKEN_WNG("WNG");
+ static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS");
+
+ switch (const auto setCommandToken = lexer.getToken(); setCommandToken.type)
+ {
+ case Token::TYPE_EOF:
+ return SetNode();
+
+ case Token::TYPE_OTHER:
+ {
+ const auto& text = setCommandToken.processedText;
+
+ if (const auto parsed = parseSet(text, TOKEN_AUTODDL, 4))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_AUTOTERM))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_BAIL))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_BLOBDISPLAY, 4))
+ return parsed.value();
+ else if (text == TOKEN_BULK_INSERT)
+ {
+ SetBulkInsertNode node;
+
+ if (const auto statement = parseUtilEof())
+ {
+ node.statement = statement.value();
+ return node;
+ }
+ }
+ else if (const auto parsed = parseSet(text, TOKEN_COUNT))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_ECHO))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_EXEC_PATH_DISPLAY))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_EXPLAIN))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_HEADING))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_KEEP_TRAN_PARAMS, 9))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_LIST))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_LOCAL_TIMEOUT))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_MAXROWS))
+ return parsed.value();
+ else if (text == TOKEN_NAMES)
+ {
+ SetNamesNode node;
+ node.name = parseName();
+
+ if (parseEof())
+ return node;
+ }
+ else if (const auto parsed = parseSet(text, TOKEN_PER_TABLE_STATS, 7))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_PLAN))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_PLANONLY))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_ROWCOUNT))
+ return parsed.value();
+ else if (text == TOKEN_SQL)
+ {
+ SetSqlDialectNode node;
+
+ if (const auto dialectToken = lexer.getToken();
+ dialectToken.type == Token::TYPE_OTHER && dialectToken.processedText == "DIALECT")
+ {
+ if (const auto arg = lexer.getToken(); arg.type != Token::TYPE_EOF)
+ {
+ node.arg = arg.processedText;
+
+ if (parseEof())
+ return node;
+ }
+ }
+ }
+ else if (const auto parsed = parseSet(text, TOKEN_SQLDA_DISPLAY))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_STATS, 4))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_TERMINATOR, 4, false))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_TIME))
+ {
+ if (const auto setTimeNode = std::get_if(&parsed.value());
+ setTimeNode && setTimeNode->arg == "ZONE")
+ {
+ return InvalidNode();
+ }
+
+ return parsed.value();
+ }
+ else if (text.length() >= 5 && std::string(TOKEN_TRANSACTION).find(text) == 0)
+ {
+ SetTransactionNode node;
+ node.statement = lexer.getBuffer();
+ return node;
+ }
+ else if (const auto parsed = parseSet(text, TOKEN_WARNINGS, 7))
+ return parsed.value();
+ else if (const auto parsed = parseSet(text, TOKEN_WNG))
+ return parsed.value();
+ else if (text == TOKEN_WIDTH)
+ {
+ SetWidthNode node;
+
+ if (const auto column = lexer.getToken(); column.type != Token::TYPE_EOF)
+ {
+ node.column = column.processedText;
+
+ if (const auto width = lexer.getToken(); width.type != Token::TYPE_EOF)
+ {
+ node.width = width.processedText;
+
+ if (!parseEof())
+ return InvalidNode();
+ }
+
+ return node;
+ }
+ }
+ else if (const auto parsed = parseSet(text, TOKEN_WIRE_STATS, 4))
+ return parsed.value();
+
+ break;
+ }
+ }
+
+ return InvalidNode();
+}
+
+template
+std::optional FrontendParser::parseSet(std::string_view setCommand,
+ std::string_view testCommand, unsigned testCommandMinLen, bool useProcessedText)
+{
+ if (setCommand == testCommand ||
+ (testCommandMinLen && setCommand.length() >= testCommandMinLen &&
+ std::string(testCommand).find(setCommand) == 0))
+ {
+ Node node;
+
+ if (const auto arg = lexer.getToken(); arg.type != Token::TYPE_EOF)
+ {
+ node.arg = useProcessedText ? arg.processedText : arg.rawText;
+
+ if (!parseEof())
+ return InvalidNode();
+ }
+
+ return node;
+ }
+
+ return std::nullopt;
+}
+
+FrontendParser::AnyShowNode FrontendParser::parseShow()
+{
+ static constexpr std::string_view TOKEN_CHECKS("CHECKS");
+ static constexpr std::string_view TOKEN_COLLATES("COLLATES");
+ static constexpr std::string_view TOKEN_COLLATIONS("COLLATIONS");
+ static constexpr std::string_view TOKEN_COMMENTS("COMMENTS");
+ static constexpr std::string_view TOKEN_DATABASE("DATABASE");
+ static constexpr std::string_view TOKEN_DEPENDENCIES("DEPENDENCIES");
+ static constexpr std::string_view TOKEN_DEPENDENCY("DEPENDENCY");
+ static constexpr std::string_view TOKEN_DOMAINS("DOMAINS");
+ static constexpr std::string_view TOKEN_EXCEPTIONS("EXCEPTIONS");
+ static constexpr std::string_view TOKEN_FILTERS("FILTERS");
+ static constexpr std::string_view TOKEN_FUNCTIONS("FUNCTIONS");
+ static constexpr std::string_view TOKEN_INDEXES("INDEXES");
+ static constexpr std::string_view TOKEN_INDICES("INDICES");
+ static constexpr std::string_view TOKEN_GENERATORS("GENERATORS");
+ static constexpr std::string_view TOKEN_GRANTS("GRANTS");
+ static constexpr std::string_view TOKEN_MAPPINGS("MAPPINGS");
+ static constexpr std::string_view TOKEN_PACKAGES("PACKAGES");
+ static constexpr std::string_view TOKEN_PROCEDURES("PROCEDURES");
+ static constexpr std::string_view TOKEN_PUBLICATIONS("PUBLICATIONS");
+ static constexpr std::string_view TOKEN_ROLES("ROLES");
+ static constexpr std::string_view TOKEN_SECCLASSES("SECCLASSES");
+ static constexpr std::string_view TOKEN_SEQUENCES("SEQUENCES");
+ static constexpr std::string_view TOKEN_SQL("SQL");
+ static constexpr std::string_view TOKEN_SYSTEM("SYSTEM");
+ static constexpr std::string_view TOKEN_TABLES("TABLES");
+ static constexpr std::string_view TOKEN_TRIGGERS("TRIGGERS");
+ static constexpr std::string_view TOKEN_USERS("USERS");
+ static constexpr std::string_view TOKEN_VER("VER");
+ static constexpr std::string_view TOKEN_VERSION("VERSION");
+ static constexpr std::string_view TOKEN_VIEWS("VIEWS");
+ static constexpr std::string_view TOKEN_WIRE_STATISTICS("WIRE_STATISTICS");
+ static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS");
+
+ switch (const auto showCommandToken = lexer.getToken(); showCommandToken.type)
+ {
+ case Token::TYPE_EOF:
+ return ShowNode();
+
+ case Token::TYPE_OTHER:
+ {
+ const auto& text = showCommandToken.processedText;
+
+ if (const auto parsed = parseShowOptName(text, TOKEN_CHECKS, 5))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_COLLATES, 7))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_COLLATIONS, 9))
+ return parsed.value();
+ else if (text.length() >= 7 && std::string(TOKEN_COMMENTS).find(text) == 0)
+ {
+ if (parseEof())
+ return ShowCommentsNode();
+ }
+ else if (text == TOKEN_DATABASE)
+ {
+ if (parseEof())
+ return ShowDatabaseNode();
+ }
+ else if (const auto parsed = parseShowOptName(text, TOKEN_DEPENDENCIES, 5))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_DEPENDENCY, 5))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_DOMAINS, 6))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_EXCEPTIONS, 5))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_FILTERS, 6))
+ return parsed.value();
+ else if (text.length() >= 4 && std::string(TOKEN_FUNCTIONS).find(text) == 0)
+ {
+ ShowFunctionsNode node;
+ node.name = parseName();
+
+ if (node.name)
+ {
+ if (const auto token = lexer.getNameToken();
+ token.type == Token::TYPE_OTHER && token.rawText == ".")
+ {
+ node.package = node.name;
+ node.name = parseName();
+
+ if (parseEof())
+ return node;
+ }
+ else if (token.type == Token::TYPE_EOF)
+ {
+ return node;
+ }
+ }
+ else
+ return node;
+ }
+ else if (const auto parsed = parseShowOptName(text, TOKEN_INDEXES, 3))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_INDICES, 0))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_GENERATORS, 3))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_GRANTS, 5))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_MAPPINGS, 3))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_PACKAGES, 4))
+ return parsed.value();
+ else if (text.length() >= 4 && std::string(TOKEN_PROCEDURES).find(text) == 0)
+ {
+ ShowProceduresNode node;
+ node.name = parseName();
+
+ if (node.name)
+ {
+ if (const auto token = lexer.getNameToken();
+ token.type == Token::TYPE_OTHER && token.rawText == ".")
+ {
+ node.package = node.name;
+ node.name = parseName();
+
+ if (parseEof())
+ return node;
+ }
+ else if (token.type == Token::TYPE_EOF)
+ {
+ return node;
+ }
+ }
+ else
+ return node;
+ }
+ else if (const auto parsed = parseShowOptName(text, TOKEN_PUBLICATIONS, 3))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_ROLES, 4))
+ return parsed.value();
+ else if (text.length() >= 6 && std::string(TOKEN_SECCLASSES).find(text) == 0)
+ {
+ const auto lexerPos = lexer.getPos();
+ const auto token = lexer.getNameToken();
+
+ ShowSecClassesNode node;
+
+ if (!(token.type == Token::TYPE_OTHER && token.rawText == "*"))
+ {
+ lexer.setPos(lexerPos);
+ node.name = parseName();
+
+ if (!node.name)
+ return InvalidNode();
+ }
+
+ const auto optDetail = parseName();
+ node.detail = optDetail == "DET" || optDetail == "DETAIL";
+
+ if (!node.detail && optDetail)
+ return InvalidNode();
+
+ if (parseEof())
+ return node;
+ }
+ else if (const auto parsed = parseShowOptName(text, TOKEN_SEQUENCES, 3))
+ return parsed.value();
+ else if (text == TOKEN_SQL)
+ {
+ if (const auto dialectToken = lexer.getToken();
+ dialectToken.type == Token::TYPE_OTHER && dialectToken.processedText == "DIALECT")
+ {
+ if (parseEof())
+ return ShowSqlDialectNode();
+ }
+ }
+ else if (text.length() >= 3 && std::string(TOKEN_SYSTEM).find(text) == 0)
+ {
+ ShowSystemNode node;
+
+ if (const auto objectType = parseName())
+ {
+ const auto objectTypeText = std::string(objectType->c_str());
+
+ if ((objectTypeText.length() >= 7 && std::string(TOKEN_COLLATES).find(objectTypeText) == 0) ||
+ (objectTypeText.length() >= 9 && std::string(TOKEN_COLLATIONS).find(objectTypeText) == 0))
+ {
+ node.objType = obj_collation;
+ }
+ else if (objectTypeText.length() >= 4 && std::string(TOKEN_FUNCTIONS).find(objectTypeText) == 0)
+ node.objType = obj_udf;
+ else if (objectTypeText.length() >= 5 && std::string(TOKEN_TABLES).find(objectTypeText) == 0)
+ node.objType = obj_relation;
+ else if (objectTypeText.length() >= 4 && std::string(TOKEN_ROLES).find(objectTypeText) == 0)
+ node.objType = obj_sql_role;
+ else if (objectTypeText.length() >= 4 && std::string(TOKEN_PROCEDURES).find(objectTypeText) == 0)
+ node.objType = obj_procedure;
+ else if (objectTypeText.length() >= 4 && std::string(TOKEN_PACKAGES).find(objectTypeText) == 0)
+ node.objType = obj_package_header;
+ else if (objectTypeText.length() >= 3 && std::string(TOKEN_PUBLICATIONS).find(objectTypeText) == 0)
+ node.objType = obj_publication;
+ else
+ return InvalidNode();
+
+ if (!parseEof())
+ return InvalidNode();
+ }
+
+ return node;
+ }
+ else if (const auto parsed = parseShowOptName(text, TOKEN_TABLES, 5))
+ return parsed.value();
+ else if (const auto parsed = parseShowOptName(text, TOKEN_TRIGGERS, 4))
+ return parsed.value();
+ else if (text == TOKEN_USERS)
+ {
+ if (parseEof())
+ return ShowUsersNode();
+ }
+ else if (text == TOKEN_VER || text == TOKEN_VERSION)
+ {
+ if (parseEof())
+ return ShowVersionNode();
+ }
+ else if (const auto parsed = parseShowOptName(text, TOKEN_VIEWS, 4))
+ return parsed.value();
+ else if (text.length() >= 9 && std::string(TOKEN_WIRE_STATISTICS).find(text) == 0 ||
+ text == TOKEN_WIRE_STATS)
+ {
+ if (parseEof())
+ return ShowWireStatsNode();
+ }
+
+ break;
+ }
+ }
+
+ return InvalidNode();
+}
+
+template
+std::optional FrontendParser::parseShowOptName(std::string_view showCommand,
+ std::string_view testCommand, unsigned testCommandMinLen)
+{
+ if (showCommand == testCommand ||
+ (testCommandMinLen && showCommand.length() >= testCommandMinLen &&
+ std::string(testCommand).find(showCommand) == 0))
+ {
+ Node node;
+ node.name = parseName();
+
+ if (!parseEof())
+ return InvalidNode();
+
+ return node;
+ }
+
+ return std::nullopt;
+}
+
+std::optional FrontendParser::parseUtilEof()
+{
+ const auto startPos = lexer.getPos();
+ auto lastPos = startPos;
+ bool first = true;
+
+ do
+ {
+ const auto token = lexer.getToken();
+
+ if (token.type == Token::TYPE_EOF)
+ {
+ if (first)
+ return std::nullopt;
+
+ return FrontendLexer::trim(std::string(startPos, lastPos));
+ }
+
+ lastPos = lexer.getPos();
+ first = false;
+ } while (true);
+
+ return std::nullopt;
+}
diff --git a/src/isql/FrontendParser.h b/src/isql/FrontendParser.h
new file mode 100644
index 0000000000..940c94f5f7
--- /dev/null
+++ b/src/isql/FrontendParser.h
@@ -0,0 +1,285 @@
+/*
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Adriano dos Santos Fernandes
+ * for the Firebird Open Source RDBMS project.
+ *
+ * Copyright (c) 2024 Adriano dos Santos Fernandes
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ *
+ */
+
+#ifndef FB_ISQL_FRONTEND_PARSER_H
+#define FB_ISQL_FRONTEND_PARSER_H
+
+#include "../isql/FrontendLexer.h"
+#include "../jrd/obj.h"
+#include "../common/classes/MetaString.h"
+#include
+#include
+#include
+#include
+#include
+
+class FrontendParser
+{
+private:
+ using Token = FrontendLexer::Token;
+
+public:
+ struct Options
+ {
+ bool schemaAsDatabase = false;
+ };
+
+ struct InvalidNode {};
+
+ struct AddNode { Firebird::MetaString tableName; };
+ struct BlobDumpViewNode { ISC_QUAD blobId; std::optional file; };
+ struct ConnectNode { std::vector args; };
+ struct CopyNode { Firebird::MetaString source; Firebird::MetaString destination; std::string database; };
+ struct CreateDatabaseNode { std::vector args; };
+ struct DropDatabaseNode {};
+ struct EditNode { std::optional file; };
+ struct ExitNode {};
+ struct ExplainNode { std::string query; };
+ struct HelpNode { std::optional command; };
+ struct InputNode { std::string file; };
+ struct OutputNode { std::optional file; };
+ struct QuitNode {};
+ struct ShellNode { std::optional command; };
+
+ struct SetNode {};
+ struct SetAutoDdlNode { std::string arg; };
+ struct SetAutoTermNode { std::string arg; };
+ struct SetBailNode { std::string arg; };
+ struct SetBlobDisplayNode { std::string arg; };
+ struct SetBulkInsertNode { std::string statement; };
+ struct SetCountNode { std::string arg; };
+ struct SetEchoNode { std::string arg; };
+ struct SetExecPathDisplayNode { std::string arg; };
+ struct SetExplainNode { std::string arg; };
+ struct SetHeadingNode { std::string arg; };
+ struct SetKeepTranParamsNode { std::string arg; };
+ struct SetListNode { std::string arg; };
+ struct SetLocalTimeoutNode { std::string arg; };
+ struct SetMaxRowsNode { std::string arg; };
+ struct SetNamesNode { std::optional name; };
+ struct SetPerTableStatsNode { std::string arg; };
+ struct SetPlanNode { std::string arg; };
+ struct SetPlanOnlyNode { std::string arg; };
+ struct SetSqldaDisplayNode { std::string arg; };
+ struct SetSqlDialectNode { std::string arg; };
+ struct SetStatsNode { std::string arg; };
+ struct SetTermNode { std::string arg; };
+ struct SetTimeNode { std::string arg; };
+ struct SetTransactionNode { std::string statement; };
+ struct SetWarningsNode { std::string arg; };
+ struct SetWidthNode { std::string column; std::string width; };
+ struct SetWireStatsNode { std::string arg; };
+
+ struct ShowNode {};
+ struct ShowChecksNode { std::optional name; };
+ struct ShowCollationsNode { std::optional name; };
+ struct ShowCommentsNode {};
+ struct ShowDatabaseNode {};
+ struct ShowDomainsNode { std::optional name; };
+ struct ShowDependenciesNode { std::optional name; };
+ struct ShowExceptionsNode { std::optional name; };
+ struct ShowFiltersNode { std::optional name; };
+ struct ShowFunctionsNode { std::optional name; std::optional package; };
+ struct ShowGeneratorsNode { std::optional name; };
+ struct ShowGrantsNode { std::optional name; };
+ struct ShowIndexesNode { std::optional name; };
+ struct ShowMappingsNode { std::optional name; };
+ struct ShowPackagesNode { std::optional name; };
+ struct ShowProceduresNode { std::optional name; std::optional package; };
+ struct ShowPublicationsNode { std::optional name; };
+ struct ShowRolesNode { std::optional name; };
+ struct ShowSecClassesNode { std::optional name; bool detail = false; };
+ struct ShowSqlDialectNode {};
+ struct ShowSystemNode { std::optional objType; };
+ struct ShowTablesNode { std::optional name; };
+ struct ShowTriggersNode { std::optional name; };
+ struct ShowUsersNode {};
+ struct ShowVersionNode {};
+ struct ShowViewsNode { std::optional name; };
+ struct ShowWireStatsNode {};
+
+ using AnySetNode = std::variant<
+ SetNode,
+
+ SetAutoDdlNode,
+ SetAutoTermNode,
+ SetBailNode,
+ SetBlobDisplayNode,
+ SetBulkInsertNode,
+ SetCountNode,
+ SetEchoNode,
+ SetExecPathDisplayNode,
+ SetExplainNode,
+ SetHeadingNode,
+ SetKeepTranParamsNode,
+ SetListNode,
+ SetLocalTimeoutNode,
+ SetMaxRowsNode,
+ SetNamesNode,
+ SetPerTableStatsNode,
+ SetPlanNode,
+ SetPlanOnlyNode,
+ SetSqldaDisplayNode,
+ SetSqlDialectNode,
+ SetStatsNode,
+ SetTermNode,
+ SetTimeNode,
+ SetTransactionNode,
+ SetWarningsNode,
+ SetWidthNode,
+ SetWireStatsNode,
+
+ InvalidNode
+ >;
+
+ using AnyShowNode = std::variant<
+ ShowNode,
+
+ ShowChecksNode,
+ ShowCollationsNode,
+ ShowCommentsNode,
+ ShowDatabaseNode,
+ ShowDomainsNode,
+ ShowDependenciesNode,
+ ShowExceptionsNode,
+ ShowFiltersNode,
+ ShowFunctionsNode,
+ ShowGeneratorsNode,
+ ShowGrantsNode,
+ ShowIndexesNode,
+ ShowMappingsNode,
+ ShowPackagesNode,
+ ShowProceduresNode,
+ ShowPublicationsNode,
+ ShowRolesNode,
+ ShowSecClassesNode,
+ ShowSqlDialectNode,
+ ShowSystemNode,
+ ShowTablesNode,
+ ShowTriggersNode,
+ ShowUsersNode,
+ ShowVersionNode,
+ ShowViewsNode,
+ ShowWireStatsNode,
+
+ InvalidNode
+ >;
+
+ using AnyNode = std::variant<
+ AddNode,
+ BlobDumpViewNode,
+ ConnectNode,
+ CopyNode,
+ CreateDatabaseNode,
+ DropDatabaseNode,
+ EditNode,
+ ExitNode,
+ ExplainNode,
+ HelpNode,
+ InputNode,
+ OutputNode,
+ QuitNode,
+ ShellNode,
+
+ AnySetNode,
+ AnyShowNode,
+
+ InvalidNode
+ >;
+
+ template
+ static inline constexpr bool AlwaysFalseV = false;
+
+public:
+ FrontendParser(std::string_view statement, const Options& aOptions)
+ : lexer(statement),
+ options(aOptions)
+ {
+ }
+
+ FrontendParser(const FrontendParser&) = delete;
+ FrontendParser& operator=(const FrontendParser&) = delete;
+
+public:
+ static AnyNode parse(std::string_view statement, const Options& options)
+ {
+ try
+ {
+ FrontendParser parser(statement, options);
+ return parser.internalParse();
+ }
+ catch (const FrontendLexer::IncompleteTokenError&)
+ {
+ return InvalidNode();
+ }
+ }
+
+private:
+ AnyNode internalParse();
+ AnySetNode parseSet();
+
+ template
+ std::optional parseSet(std::string_view setCommand,
+ std::string_view testCommand, unsigned testCommandMinLen = 0, bool useProcessedText = true);
+
+ AnyShowNode parseShow();
+
+ template
+ std::optional parseShowOptName(std::string_view showCommand,
+ std::string_view testCommand, unsigned testCommandMinLen = 0);
+
+ bool parseEof()
+ {
+ return lexer.getToken().type == Token::TYPE_EOF;
+ }
+
+ std::optional parseName()
+ {
+ const auto token = lexer.getNameToken();
+
+ if (token.type != Token::TYPE_EOF)
+ return Firebird::MetaString(token.processedText.c_str());
+
+ return std::nullopt;
+ }
+
+ std::optional parseFileName()
+ {
+ const auto token = lexer.getToken();
+
+ if (token.type == Token::TYPE_STRING || token.type == Token::TYPE_META_STRING)
+ return token.processedText;
+ else if (token.type != Token::TYPE_EOF)
+ return token.rawText;
+
+ return std::nullopt;
+ }
+
+ std::optional parseUtilEof();
+
+private:
+ FrontendLexer lexer;
+ const Options options;
+};
+
+#endif // FB_ISQL_FRONTEND_PARSER_H
diff --git a/src/isql/isql.epp b/src/isql/isql.epp
index 8de2929f97..7370e160fe 100644
--- a/src/isql/isql.epp
+++ b/src/isql/isql.epp
@@ -55,6 +55,8 @@
#include
#include
#include "../isql/FrontendLexer.h"
+#include "../isql/FrontendParser.h"
+#include "../common/StdHelper.h"
#include "../common/utils_proto.h"
#include "../common/classes/array.h"
#include "../common/classes/init.h"
@@ -450,30 +452,24 @@ struct ri_actions
const SCHAR* ri_action_print_mixed;
};
-static processing_state add_row(TEXT*);
-static processing_state blobedit(const TEXT*, const TEXT* const*);
+static processing_state add_row(const MetaString&);
+static processing_state blobedit(ISC_QUAD, const char*);
static processing_state bulk_insert_hack(const char* command);
static bool bulk_insert_retriever(const char* prompt);
static void check_autoterm();
static bool check_date(const tm& times);
static bool check_time(const tm& times);
static bool check_timestamp(const tm& times, const int msec);
-static size_t chop_at(char target[], const size_t size);
-static void col_check(const TEXT*, unsigned*);
-static processing_state copy_table(TEXT*, TEXT*, TEXT*);
-static processing_state create_db(const TEXT*, TEXT*);
+static void col_check(const MetaString&, unsigned*);
+static processing_state copy_table(const MetaString&, const MetaString&, const TEXT*);
+static processing_state create_db(const FrontendParser::CreateDatabaseNode& node);
static void do_isql();
static processing_state drop_db();
-static processing_state edit(const TEXT* const*);
+static processing_state edit(const TEXT*);
static processing_state end_trans();
static processing_state escape(const TEXT*);
static processing_state execSetDebugCommand();
-static processing_state frontend(const TEXT*);
-static processing_state frontend_set(const char* cmd, const char* const* parms,
- const char* const* lparms, char* const bad_dialect_buf, bool& bad_dialect);
-static void frontend_free_parms(TEXT*[], TEXT*[], TEXT parm_defaults[][1]);
-static void frontend_load_parms(const TEXT* p, TEXT* parms[], TEXT* lparms[],
- TEXT parm_defaults[][1]);
+static processing_state frontend(const std::string&);
static processing_state do_set_command(const TEXT*, bool*);
static processing_state get_dialect(const char* const dialect_str,
char* const bad_dialect_buf, bool& bad_dialect);
@@ -483,16 +479,13 @@ static processing_state print_sets();
static processing_state explain(const TEXT*);
static processing_state help(const TEXT*);
static bool isyesno(const TEXT*);
-static processing_state newdb(TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool);
+static processing_state newdb(const TEXT*, const TEXT*, const TEXT*, int, const TEXT*, bool);
static processing_state newinput(const TEXT*);
static processing_state newoutput(const TEXT*);
static processing_state newsize(const TEXT*, const TEXT*);
static processing_state newMaxRows(const TEXT* newMaxRowsStr);
static processing_state newtrans(const TEXT*);
static processing_state parse_arg(int, SCHAR**, SCHAR*); //, FILE**);
-#ifdef DEV_BUILD
-static processing_state passthrough(const char* cmd);
-#endif
static unsigned print_item(TEXT**, const IsqlVar*, const unsigned);
static void print_item_numeric(SINT64, int, int, TEXT*);
static processing_state print_line(Firebird::IMessageMetadata*, UCHAR*, const unsigned pad[], TEXT line[]);
@@ -2388,7 +2381,7 @@ static string get_numeric_value(const char* fromStr)
}
-static processing_state add_row(TEXT* tabname)
+static processing_state add_row(const MetaString& tabname)
{
/**************************************
*
@@ -2407,7 +2400,7 @@ static processing_state add_row(TEXT* tabname)
* tabname -- Name of table to insert into
*
**************************************/
- if (!*tabname)
+ if (tabname.isEmpty())
return (ps_ERR);
if (!Interactive)
@@ -2428,13 +2421,9 @@ static processing_state add_row(TEXT* tabname)
return FAIL;
}
- chop_at(tabname, QUOTED_NAME_SIZE);
- if (tabname[0] != DBL_QUOTE)
- IUTILS_make_upper(tabname);
-
// Query to obtain relation information
string str2;
- str2.printf("SELECT * FROM %s", tabname);
+ str2.printf("SELECT * FROM %s", IUTILS_name_to_string(tabname).c_str());
if (global_Stmt)
{
@@ -2472,7 +2461,7 @@ static processing_state add_row(TEXT* tabname)
// There is a question mark for each column that's known to be updatable.
- insertstring.printf("INSERT INTO %s (", tabname);
+ insertstring.printf("INSERT INTO %s (", IUTILS_name_to_string(tabname).c_str());
unsigned i_cols = 0;
{ // scope
@@ -2999,7 +2988,7 @@ static processing_state add_row(TEXT* tabname)
}
-static processing_state blobedit(const TEXT* action, const TEXT* const* cmd)
+static processing_state blobedit(ISC_QUAD blobId, const char* path)
{
/**************************************
*
@@ -3016,20 +3005,14 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd)
if (!ISQL_dbcheck())
return FAIL;
- if (*cmd[1] == 0)
+ if (blobId.gds_quad_high == 0 && blobId.gds_quad_low == 0)
return ps_ERR;
- const TEXT* p = cmd[1];
-
- // Find the high and low values of the blob id
- ISC_QUAD blobid;
- sscanf(p, "%" xLONGFORMAT":%" xLONGFORMAT, &blobid.gds_quad_high, &blobid.gds_quad_low);
-
// If it isn't an explicit blobedit, then do a dump. Since this is a
// user operation, put it on the M__trans handle.
processing_state rc = SKIP;
- if (!strcmp(action, "BLOBVIEW"))
+ if (!path)
{
Firebird::UtilInterfacePtr utl;
PathName tmpf = TempFile::create(fbStatus, "blob");
@@ -3037,7 +3020,7 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd)
return ps_ERR;
const char* filename = tmpf.c_str();
- utl->dumpBlob(fbStatus, &blobid, DB, M__trans, filename, FB_TRUE);
+ utl->dumpBlob(fbStatus, &blobId, DB, M__trans, filename, FB_TRUE);
if (ISQL_errmsg(fbStatus))
rc = ps_ERR;
else
@@ -3045,14 +3028,12 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd)
unlink(filename);
}
- else if ((!strcmp(action, "BLOBDUMP")) && (*cmd[2]))
+ else if (*path)
{
// If this is a blobdump, make sure there is a file name
// We can't be sure if the BLOB is TEXT or BINARY data,
// as we're not sure, we'll dump it in BINARY mode.
- TEXT path[MAXPATHLEN];
- strip_quotes(cmd[2], path);
- Firebird::UtilInterfacePtr()->dumpBlob(fbStatus, &blobid, DB, M__trans, path, FB_FALSE);
+ Firebird::UtilInterfacePtr()->dumpBlob(fbStatus, &blobId, DB, M__trans, path, FB_FALSE);
}
else
rc = ps_ERR;
@@ -3096,14 +3077,6 @@ static processing_state blobedit(const TEXT* action, const TEXT* const* cmd)
// If you came here looking for robust parsing code, you're at the wrong place.
static processing_state bulk_insert_hack(const char* command)
{
- // Skip "SET BULK_INSERT" part.
- for (int j = 0; j < 2; ++j)
- {
- while (*command && *command != 0x20)
- ++command;
- while (*command && *command == 0x20)
- ++command;
- }
if (!*command)
return ps_ERR;
@@ -4095,23 +4068,7 @@ static bool check_timestamp(const tm& times, const int msec)
}
-// *************
-// c h o p _ a t
-// *************
-// Simply ensure a given string argument fits in a size, terminator included.
-static size_t chop_at(char target[], const size_t size)
-{
- size_t len = strlen(target);
- if (len >= size)
- {
- len = size - 1;
- target[len] = 0;
- }
- return len;
-}
-
-
-static void col_check(const TEXT* tabname, unsigned* colnumber)
+static void col_check(const MetaString& tabname, unsigned* colnumber)
{
/**************************************
*
@@ -4132,7 +4089,7 @@ static void col_check(const TEXT* tabname, unsigned* colnumber)
FOR F IN RDB$FIELDS CROSS
R IN RDB$RELATION_FIELDS WITH
F.RDB$FIELD_NAME = R.RDB$FIELD_SOURCE AND
- R.RDB$RELATION_NAME EQ tabname
+ R.RDB$RELATION_NAME EQ tabname.c_str()
SORTED BY R.RDB$FIELD_POSITION, R.RDB$FIELD_NAME
if ((!F.RDB$DIMENSIONS.NULL && F.RDB$DIMENSIONS) || (!F.RDB$COMPUTED_BLR.NULL))
@@ -4147,9 +4104,7 @@ static void col_check(const TEXT* tabname, unsigned* colnumber)
}
-static processing_state copy_table(TEXT* source,
- TEXT* destination,
- TEXT* otherdb)
+static processing_state copy_table(const MetaString& source, const MetaString& destination, const TEXT* otherdb)
{
/**************************************
*
@@ -4164,7 +4119,7 @@ static processing_state copy_table(TEXT* source,
* destination == name of newly created table
*
**************************************/
- if (!source[0] || !destination[0])
+ if (source.isEmpty() || destination.isEmpty())
{
STDERROUT("Either source or destination tables are missing");
return SKIP;
@@ -4193,43 +4148,9 @@ static processing_state copy_table(TEXT* source,
return END;
}
- chop_at(source, QUOTED_NAME_SIZE);
- if (source[0] != DBL_QUOTE)
- IUTILS_make_upper(source);
- /*
- chop_at(source_tbl, MAX_SQL_IDENTIFIER_SIZE);
- TEXT source[QUOTED_NAME_SIZE];
- bool delimited_yes = source_tbl[0] == DBL_QUOTE;
- if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) {
- IUTILS_copy_SQL_id(source_tbl, source, DBL_QUOTE);
- }
- else
+ if (EXTRACT_list_table(source.c_str(), destination.c_str(), domain_flag, -1))
{
- strcpy(source, source_tbl);
- IUTILS_make_upper(source);
- }
- */
-
- chop_at(destination, QUOTED_NAME_SIZE);
- if (destination[0] != DBL_QUOTE)
- IUTILS_make_upper(destination);
- /*
- chop_at(destination_tbl, MAX_SQL_IDENTIFIER_SIZE);
- TEXT destination[QUOTED_NAME_SIZE];
- delimited_yes = destination_tbl[0] == DBL_QUOTE;
- if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION && delimited_yes) {
- IUTILS_copy_SQL_id(destination_tbl, destination, DBL_QUOTE);
- }
- else
- {
- strcpy(destination, destination_tbl);
- IUTILS_make_upper(destination);
- }
- */
-
- if (EXTRACT_list_table(source, destination, domain_flag, -1))
- {
- IUTILS_msg_get(NOT_FOUND, errbuf, SafeArg() << source);
+ IUTILS_msg_get(NOT_FOUND, errbuf, SafeArg() << source.c_str());
STDERROUT(errbuf);
fclose(isqlGlob.Out);
}
@@ -4245,7 +4166,7 @@ static processing_state copy_table(TEXT* source,
sprintf(cmd, "isql -q %s -i %s", altdb, ftmp.c_str());
if (system(cmd))
{
- IUTILS_msg_get(COPY_ERR, errbuf, SafeArg() << destination << altdb);
+ IUTILS_msg_get(COPY_ERR, errbuf, SafeArg() << destination.c_str() << altdb);
STDERROUT(errbuf);
}
}
@@ -4257,21 +4178,7 @@ static processing_state copy_table(TEXT* source,
}
-static void appendClause(string& to, const char* label, const TEXT* value, char quote = 0)
-{
- to += ' ';
- to += label;
- to += ' ';
- if (quote)
- to += quote;
- to += value;
- if (quote)
- to += quote;
- to += ' ';
-}
-
-
-static processing_state create_db(const TEXT* statement, TEXT* d_name)
+static processing_state create_db(const FrontendParser::CreateDatabaseNode& node)
{
/**************************************
*
@@ -4283,109 +4190,37 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name)
* Intercept create database commands to
* adjust the DB and transaction handles
*
- * Parameters: statement == the entire statement for processing.
- *
* Note: SQL ROLE setting must be taken into an account no matter
* that the newly created database will not have any user roles defined
* in it. Role may affect right to create new database.
*
**************************************/
+
processing_state ret = SKIP;
// Disconnect from the database and cleanup
ISQL_disconnect_database(false);
- // Parse statement to tokens
- const char* quotes = "\"'";
- string nlStatement(statement);
- nlStatement += '\n';
- Firebird::Tokens toks;
- toks.quotes(quotes);
- toks.parse(0, nlStatement.c_str());
-
- const unsigned KEY_USER = 0;
- const unsigned KEY_PASS = 1;
- const unsigned KEY_ROLE = 2;
- const unsigned KEY_NAMES = 3;
- const unsigned KEY_SET = 4;
-
- struct Key
+ for (const auto createWithRole : {true, false})
{
- const char* text;
- bool has;
- };
+ std::string statement = "create database " + node.args[0].rawText;
- Key keys[5] = {
- { "USER", false },
- { "PASSWORD", false },
- { "ROLE", false },
- { "NAMES", false },
- { "SET", false }
- };
+ // If there are global parameters, we will set them into the create statement.
- for (unsigned t = 0; t < toks.getCount(); ++t)
- {
- Firebird::NoCaseString token(toks[t].text, toks[t].length);
- unsigned k;
+ if (global_usr)
+ statement += std::string(" USER ") + isqlGlob.User;
- for (k = 0; k < FB_NELEM(keys); ++k)
- {
- if (token == keys[k].text)
- {
- if (k != KEY_NAMES || keys[KEY_SET].has)
- keys[k].has = true;
- break;
- }
- }
+ if (global_psw)
+ statement += std::string(" PASSWORD '") + Password + "'";
- if (k != KEY_SET)
- keys[KEY_SET].has = false;
- }
+ if (global_role && createWithRole)
+ statement += std::string(" ROLE ") + isqlGlob.Role;
- for (int createWithoutRole = 0; createWithoutRole < 2; ++createWithoutRole)
- {
- ret = SKIP;
- TEXT usr[USER_LENGTH];
- TEXT psw[PASSWORD_LENGTH];
- TEXT role[ROLE_LENGTH];
+ if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET) != 0)
+ statement += std::string(" SET NAMES '") + setValues.ISQL_charset + "'";
- string modifiedCreateStatement(statement);
-
- TEXT quote = DBL_QUOTE;
- const TEXT* p = NULL;
-
- // If there is a global parameter, we will set it into the create stmt.
- if (global_usr || global_role || global_psw ||
- (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET)))
- {
- strcpy(usr, isqlGlob.User);
- strip_quotes(Password, psw);
- strcpy(role, isqlGlob.Role);
-
- string clauses;
-
- if (global_usr && !keys[KEY_USER].has)
- appendClause(clauses, keys[KEY_USER].text, usr);
-
- if (global_psw && !keys[KEY_PASS].has)
- appendClause(clauses, keys[KEY_PASS].text, psw, SINGLE_QUOTE);
-
- if (global_role && (!keys[KEY_ROLE].has) && createWithoutRole == 0)
- appendClause(clauses, keys[KEY_ROLE].text, role);
-
- if (*setValues.ISQL_charset && strcmp(setValues.ISQL_charset, DEFCHARSET)&& !keys[KEY_NAMES].has)
- {
- string setNames = keys[KEY_SET].text;
- setNames += ' ';
- setNames += keys[KEY_NAMES].text;
- appendClause(clauses, setNames.c_str(), setValues.ISQL_charset, SINGLE_QUOTE);
- }
-
- if (toks.getCount() > 3)
- modifiedCreateStatement.insert(toks[3].origin, clauses);
- else
- modifiedCreateStatement += clauses;
- }
+ for (std::size_t i = 1; i < node.args.size(); ++i)
+ statement += std::string(" ") + node.args[i].rawText;
// execute the create statement
// If the isqlGlob.SQL_dialect is not set or set to 2, create the database
@@ -4394,19 +4229,18 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name)
(isqlGlob.SQL_dialect == 0 || isqlGlob.SQL_dialect == SQL_DIALECT_V6_TRANSITION) ?
requested_SQL_dialect : isqlGlob.SQL_dialect;
- DB = Firebird::UtilInterfacePtr()->executeCreateDatabase(fbStatus, modifiedCreateStatement.length(),
- modifiedCreateStatement.c_str(), dialect, NULL);
+ DB = Firebird::UtilInterfacePtr()->executeCreateDatabase(fbStatus, statement.length(),
+ statement.c_str(), dialect, nullptr);
- if ((!DB) && (createWithoutRole == 0) && (fbStatus->getErrors()[1] == isc_dsql_error))
+ if (!DB && createWithRole && fbStatus->getErrors()[1] == isc_dsql_error)
{
- // OLd server failed to parse ROLE clause
+ // Old server failed to parse ROLE clause
continue;
}
if (ISQL_errmsg(fbStatus))
- {
ret = FAIL;
- }
+
break;
}
@@ -4423,12 +4257,7 @@ static processing_state create_db(const TEXT* statement, TEXT* d_name)
// CVC: Someone may decide to play strange games with undocumented ability
// to write crap between CREATE DATABASE and the db name, as described by
// Helen on CORE-932. Let's see if we can discover the real db name.
- string s;
- if (toks.getCount() > 2)
- s.assign(toks[2].text, toks[2].length);
- else
- s = d_name;
- strip_quotes(s.c_str(), isqlGlob.global_Db_name);
+ strcpy(isqlGlob.global_Db_name, node.args[0].getProcessedString().c_str());
ISQL_get_version(true);
@@ -4792,7 +4621,7 @@ static processing_state drop_db()
}
-static processing_state edit(const TEXT* const* cmd)
+static processing_state edit(const TEXT* file)
{
/**************************************
*
@@ -4810,9 +4639,6 @@ static processing_state edit(const TEXT* const* cmd)
**************************************/
// Set up editing command for shell
-
- const TEXT* file = cmd[1];
-
// If there is a file name specified, try to open it
processing_state rc = SKIP;
@@ -4998,9 +4824,6 @@ static processing_state escape(const TEXT* cmd)
const TEXT* shellcmd = cmd;
- // Search past the 'shell' keyword
- shellcmd += strlen("shell");
-
// Eat whitespace at beginning of command
while (*shellcmd && fb_utils::isspace(*shellcmd))
shellcmd++;
@@ -5058,7 +4881,7 @@ static processing_state execSetDebugCommand()
}
-static processing_state frontend(const TEXT* statement)
+static processing_state frontend(const std::string& statement)
{
/**************************************
*
@@ -5075,186 +4898,57 @@ static processing_state frontend(const TEXT* statement)
*
**************************************/
- class FrontOptions : public OptionsBase
- {
- public:
- enum front_commands
- {
- show, add, copy,
- blobview, output, shell, set, create, drop, connect,
- edit, input, quit, exit, explain, help,
-#ifdef DEV_BUILD
- passthrough,
-#endif
- wrong
- };
- FrontOptions(const optionsMap* inmap, size_t insize, int wrongval)
- : OptionsBase(inmap, insize, wrongval)
- {}
- };
+ FrontendParser::Options parserOptions;
+ parserOptions.schemaAsDatabase = isqlGlob.major_ods > 0 && isqlGlob.major_ods < ODS_VERSION12;
- static const FrontOptions::optionsMap options[] =
- {
- {FrontOptions::show, "SHOW", 0},
- {FrontOptions::add, "ADD", 0},
- {FrontOptions::copy, "COPY", 0},
- {FrontOptions::blobview, "BLOBVIEW", 0},
- {FrontOptions::blobview, "BLOBDUMP", 0},
- //{FrontOptions::output, "OUT", },
- {FrontOptions::output, "OUTPUT", 3},
- {FrontOptions::shell, "SHELL", 0},
- {FrontOptions::set, "SET", 0},
- {FrontOptions::create, "CREATE", 0},
- {FrontOptions::drop, "DROP", 0},
- {FrontOptions::connect, "CONNECT", 0},
- {FrontOptions::edit, "EDIT", 0},
- //{FrontOptions::input, "IN", },
- {FrontOptions::input, "INPUT", 2},
- {FrontOptions::quit, "QUIT", 0},
- {FrontOptions::exit, "EXIT", 0},
- {FrontOptions::explain, "EXPLAIN", 0},
- {FrontOptions::help, "?", 0},
- {FrontOptions::help, "HELP", 0}
-#ifdef DEV_BUILD
- ,
- {FrontOptions::passthrough, "PASSTHROUGH", 0}
-#endif
- };
-
- TEXT errbuf[MSG_LENGTH];
-
- // Store the first NUM_TERMS words as they appear in parms, using blanks
- // to delimit. Each word beyond a real word gets a null char
- // Shift parms to upper case, leaving original case in lparms
- typedef TEXT* isql_params_t[MAX_TERMS];
- isql_params_t parms, lparms;
- for (FB_SIZE_T iter = 0; iter < FB_NELEM(lparms); ++iter)
- {
- lparms[iter] = NULL;
- parms[iter] = NULL;
- }
- TEXT parm_defaults[MAX_TERMS][1];
-
- // Any whitespace and comments at the beginning are already swallowed by get_statement()
-
- // Set beginning of statement past comment
- const TEXT* const cmd = statement;
- if (!*cmd)
- {
- // In case any default transaction was started - commit it here
- if (fbTrans)
- commit_trans(&fbTrans);
-
- return SKIP;
- }
-
- frontend_load_parms(statement, parms, lparms, parm_defaults);
-
- char bad_dialect_buf[512];
+ const auto node = FrontendParser::parse(statement, parserOptions);
bool bad_dialect = false;
+ char bad_dialect_buf[512];
- // Look to see if the words (parms) match any known verbs. If nothing
- // matches then just hand the statement to process_statement
- processing_state ret = SKIP;
- const FrontOptions frontoptions(options, FB_NELEM(options), FrontOptions::wrong);
- switch (frontoptions.getCommand(parms[0]))
- {
- case FrontOptions::show:
- if (DB && !frontendTransaction())
+ const auto ret = std::visit(StdVisitOverloads{
+ [](const FrontendParser::InvalidNode&)
{
- // Free the frontend command
- frontend_free_parms(parms, lparms, parm_defaults);
- return FAIL;
- }
+ return CONT;
+ },
- ret = SHOW_metadata(parms, lparms);
- if (fbTrans)
- commit_trans(&fbTrans);
- break;
-
- case FrontOptions::add:
- if (!frontendTransaction())
+ [](const FrontendParser::AddNode& node)
{
- // Free the frontend command
- frontend_free_parms(parms, lparms, parm_defaults);
- return FAIL;
- }
+ if (!frontendTransaction())
+ return FAIL;
- ret = add_row(lparms[1]);
- if (fbTrans)
- commit_trans(&fbTrans);
- break;
+ const auto ret = add_row(node.tableName);
+ if (fbTrans)
+ commit_trans(&fbTrans);
- case FrontOptions::copy:
- if (!frontendTransaction())
+ return ret;
+ },
+
+ [](const FrontendParser::BlobDumpViewNode& node)
{
- // Free the frontend command
- frontend_free_parms(parms, lparms, parm_defaults);
- return FAIL;
- }
+ return blobedit(node.blobId, (node.file ? node.file->c_str() : nullptr));
+ },
- ret = copy_table(lparms[1], lparms[2], lparms[3]);
- if (fbTrans)
- commit_trans(&fbTrans);
- break;
-
- case FrontOptions::blobview:
- ret = blobedit(parms[0], lparms);
- break;
-
- case FrontOptions::output:
- ret = newoutput(lparms[1]);
- break;
-
- case FrontOptions::shell:
- ret = escape(cmd);
- break;
-
- case FrontOptions::set:
- ret = frontend_set(cmd, parms, lparms, bad_dialect_buf, bad_dialect);
- break;
-
- case FrontOptions::create:
- if (!strcmp(parms[1], "DATABASE") ||
- (!strcmp(parms[1], "SCHEMA") && isqlGlob.major_ods > 0 && isqlGlob.major_ods < ODS_VERSION12))
+ [](const FrontendParser::ConnectNode& node)
{
- ret = create_db(cmd, lparms[2]);
- }
- else
- ret = CONT;
- break;
-
- case FrontOptions::drop:
- if (!strcmp(parms[1], "DATABASE") ||
- (!strcmp(parms[1], "SCHEMA") && isqlGlob.major_ods > 0 && isqlGlob.major_ods < ODS_VERSION12))
- {
- if (*parms[2])
- ret = ps_ERR;
- else
- ret = drop_db();
- }
- else
- ret = CONT;
- break;
-
- case FrontOptions::connect:
- {
- const TEXT* psw = NULL;
- const TEXT* usr = NULL;
- const TEXT* sql_role_nm = NULL;
+ std::optional psw;
+ const char* usr = nullptr;
+ const char* sql_role_nm = nullptr;
int numbufs = 0;
// if a parameter is given in the command more than once, the
// last one will be used. The parameters can appear each any
// order, but each must provide a value.
- ret = SKIP;
- for (int i = 2; i < (MAX_TERMS - 1);)
+ auto ret = SKIP;
+
+ for (std::size_t i = 1; i < node.args.size() - 1;)
{
- if (!strcmp(parms[i], "CACHE") && *lparms[i + 1])
+ const auto& clause = node.args[i].processedText;
+
+ if (clause == "CACHE")
{
char* err;
- long value = strtol(lparms[i + 1], &err, 10);
+ long value = strtol(node.args[i + 1].processedText.c_str(), &err, 10);
if (*err || (value <= 0) || (value >= INT_MAX))
{
ret = ps_ERR;
@@ -5263,85 +4957,387 @@ static processing_state frontend(const TEXT* statement)
numbufs = (int) value;
i += 2;
}
- else if (!strcmp(parms[i], "USER") && *lparms[i + 1])
+ else if (clause == "USER")
{
- usr = lparms[i + 1];
+ usr = node.args[i + 1].processedText.c_str();
i += 2;
}
- else if (!strcmp(parms[i], "PASSWORD") && *lparms[i + 1])
+ else if (clause == "PASSWORD")
{
- psw = lparms[i + 1];
+ psw = node.args[i + 1].getProcessedString();
i += 2;
}
- else if (!strcmp(parms[i], "ROLE") && *lparms[i + 1])
+ else if (clause == "ROLE")
{
- sql_role_nm = lparms[i + 1];
+ sql_role_nm = node.args[i + 1].processedText.c_str();
i += 2;
}
- else if (*parms[i])
+ else if (!clause.empty())
{
// Unrecognized option to CONNECT
ret = ps_ERR;
break;
}
else
- i++;
+ ++i;
}
+
if (ret != ps_ERR)
- ret = newdb(lparms[1], usr, psw, numbufs, sql_role_nm, true);
+ {
+ ret = newdb(
+ node.args[0].getProcessedString().c_str(),
+ usr,
+ (psw ? psw->c_str() : nullptr),
+ numbufs,
+ sql_role_nm,
+ true);
+ }
+
+ return SKIP;
+ },
+
+ [](const FrontendParser::CopyNode& node)
+ {
+ if (!frontendTransaction())
+ return FAIL;
+
+ const auto ret = copy_table(node.source, node.destination, node.database.c_str());
+
+ if (fbTrans)
+ commit_trans(&fbTrans);
+
+ return ret;
+ },
+
+ [](const FrontendParser::CreateDatabaseNode& node)
+ {
+ return create_db(node);
+ },
+
+ [](const FrontendParser::DropDatabaseNode&)
+ {
+ return drop_db();
+ },
+
+ [](const FrontendParser::EditNode& node)
+ {
+ return edit(node.file.value_or("").c_str());
+ },
+
+ [](const FrontendParser::ExitNode& node)
+ {
+ return EXIT;
+ },
+
+ [](const FrontendParser::ExplainNode& node)
+ {
+ return explain(node.query.c_str());
+ },
+
+ [](const FrontendParser::HelpNode& node)
+ {
+ return help(node.command.value_or("").c_str());
+ },
+
+ [](const FrontendParser::InputNode& node)
+ {
+ return newinput(node.file.c_str());
+ },
+
+ [](const FrontendParser::OutputNode& node)
+ {
+ return newoutput(node.file.value_or("").c_str());
+ },
+
+ [](const FrontendParser::QuitNode& node)
+ {
+ return BACKOUT;
+ },
+
+ [](const FrontendParser::ShellNode& node)
+ {
+ return escape(node.command.value_or("").c_str());
+ },
+
+ // SET commands
+
+ [&](const FrontendParser::AnySetNode& anySet)
+ {
+ return std::visit(StdVisitOverloads{
+ [](const FrontendParser::InvalidNode&)
+ {
+ return CONT;
+ },
+
+ [](const FrontendParser::SetNode&)
+ {
+ return print_sets();
+ },
+
+ [](const FrontendParser::SetAutoDdlNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.Autocommit);
+ },
+
+ [](const FrontendParser::SetAutoTermNode& node)
+ {
+ const auto ret = do_set_command(node.arg.c_str(), &setValues.AutoTerm);
+ if (setValues.AutoTerm)
+ {
+ isqlGlob.Termlen = 1;
+ strcpy(isqlGlob.global_Term, ";");
+ }
+ return ret;
+ },
+
+ [](const FrontendParser::SetBailNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.BailOnError);
+ },
+
+ [](const FrontendParser::SetBlobDisplayNode& node)
+ {
+ // No arg means turn off blob display
+ if (node.arg.empty() || node.arg == "OFF")
+ setValues.Doblob = NO_BLOBS;
+ else if (node.arg == "ALL")
+ setValues.Doblob = ALL_BLOBS;
+ else
+ setValues.Doblob = atoi(node.arg.c_str());
+ return SKIP;
+ },
+
+ [](const FrontendParser::SetBulkInsertNode& node)
+ {
+ return bulk_insert_hack(node.statement.c_str());
+ },
+
+ [](const FrontendParser::SetCountNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.Docount);
+ },
+
+ [](const FrontendParser::SetEchoNode& node)
+ {
+ const auto ret = do_set_command(node.arg.c_str(), &setValues.Echo);
+ if (!setValues.Echo)
+ ISQL_prompt("");
+ return ret;
+ },
+
+ [](const FrontendParser::SetExecPathDisplayNode& node)
+ {
+ if (node.arg.empty())
+ return ps_ERR;
+ else if (node.arg == "OFF")
+ setValues.ExecPathDisplay[0] = 0;
+ else
+ {
+ static constexpr UCHAR execPath[] = {isc_info_sql_exec_path_blr_text};
+
+ if (node.arg != "BLR")
+ return ps_ERR;
+
+ memcpy(setValues.ExecPathDisplay, execPath, FB_NELEM(execPath));
+ setValues.ExecPathDisplay[FB_NELEM(execPath)] = 0;
+ }
+
+ return execSetDebugCommand();
+ },
+
+ [](const FrontendParser::SetExplainNode& node)
+ {
+ auto ret = do_set_command(node.arg.c_str(), &setValues.ExplainPlan);
+ if (setValues.ExplainPlan)
+ ret = do_set_command("ON", &setValues.Plan);
+ return ret;
+ },
+
+ [](const FrontendParser::SetHeadingNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.Heading);
+ },
+
+ [](const FrontendParser::SetKeepTranParamsNode& node)
+ {
+ const bool oldValue = setValues.KeepTranParams;
+ const auto ret = do_set_command(node.arg.c_str(), &setValues.KeepTranParams);
+ if ((ret != ps_ERR) && (oldValue != setValues.KeepTranParams))
+ TranParams->assign(setValues.KeepTranParams ? DEFAULT_DML_TRANS_SQL : "");
+ return ret;
+ },
+
+ [](const FrontendParser::SetListNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.List);
+ },
+
+ [](const FrontendParser::SetLocalTimeoutNode& node)
+ {
+ int val = strtol(node.arg.c_str(), nullptr, 10);
+
+ if (val < 0)
+ return ps_ERR;
+ else
+ {
+ setValues.StmtTimeout = val;
+ return SKIP;
+ }
+ },
+
+ [](const FrontendParser::SetMaxRowsNode& node)
+ {
+ return newMaxRows((node.arg.empty() ? "0" : node.arg.c_str()));
+ },
+
+ [](const FrontendParser::SetNamesNode& node)
+ {
+ const auto setNames = node.name ? node.name->c_str() : DEFCHARSET;
+
+ const size_t lgth = strlen(setNames);
+ if (lgth < MAXCHARSET_SIZE)
+ strcpy(setValues.ISQL_charset, setNames);
+ else
+ fb_utils::copy_terminate(setValues.ISQL_charset, setNames, MAXCHARSET_SIZE);
+
+ return SKIP;
+ },
+
+ [](const FrontendParser::SetPerTableStatsNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.PerTableStats);
+ },
+
+ [](const FrontendParser::SetPlanNode& node)
+ {
+ auto ret = do_set_command(node.arg.c_str(), &setValues.Plan);
+ if (setValues.Planonly && !setValues.Plan)
+ ret = do_set_command("OFF", &setValues.Planonly);
+ return ret;
+ },
+
+ [](const FrontendParser::SetPlanOnlyNode& node)
+ {
+ auto ret = do_set_command(node.arg.c_str(), &setValues.Planonly);
+ if (setValues.Planonly && !setValues.Plan)
+ {
+ // turn on plan
+ ret = do_set_command("ON", &setValues.Plan);
+ }
+ return ret;
+ },
+
+ [](const FrontendParser::SetSqldaDisplayNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.Sqlda_display);
+ },
+
+ [&](const FrontendParser::SetSqlDialectNode& node)
+ {
+ return get_dialect(node.arg.c_str(), bad_dialect_buf, bad_dialect);
+ },
+
+ [](const FrontendParser::SetStatsNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.Stats);
+ },
+
+ [](const FrontendParser::SetTermNode& node)
+ {
+ const TEXT* termStr = node.arg.empty() ? DEFTERM : node.arg.c_str();
+
+ for (size_t iter = 0; iter < sizeof(FORBIDDEN_TERM_CHARS); ++iter)
+ {
+ if (strchr(termStr, FORBIDDEN_TERM_CHARS[iter]))
+ {
+ TEXT msg_string[MSG_LENGTH];
+ IUTILS_msg_get(INVALID_TERM_CHARS, msg_string, SafeArg() << FORBIDDEN_TERM_CHARS_DISPLAY);
+ isqlGlob.printf("%s\n", msg_string);
+ return ps_ERR;
+ }
+ }
+
+ setValues.AutoTerm = false;
+
+ isqlGlob.Termlen = strlen(termStr);
+ if (isqlGlob.Termlen < MAXTERM_SIZE)
+ strcpy(isqlGlob.global_Term, termStr);
+ else
+ {
+ isqlGlob.Termlen = MAXTERM_SIZE - 1;
+ fb_utils::copy_terminate(isqlGlob.global_Term, termStr, isqlGlob.Termlen + 1);
+ }
+
+ return SKIP;
+ },
+
+ [](const FrontendParser::SetTimeNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.Time_display);
+ },
+
+ [](const FrontendParser::SetTransactionNode& node)
+ {
+ return newtrans(node.statement.c_str());
+ },
+
+ [](const FrontendParser::SetWarningsNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.Warnings);
+ },
+
+ [](const FrontendParser::SetWidthNode& node)
+ {
+ return newsize(node.column.c_str(), node.width.c_str());
+ },
+
+ [](const FrontendParser::SetWireStatsNode& node)
+ {
+ return do_set_command(node.arg.c_str(), &setValues.WireStats);
+ },
+
+ [](auto& arg)
+ {
+ static_assert(FrontendParser::AlwaysFalseV,
+ "Add visitor method for that set node type");
+ }
+ }, anySet);
+ },
+
+ // SHOW commands
+
+ [](const FrontendParser::AnyShowNode& node)
+ {
+ if (DB && !frontendTransaction())
+ return FAIL;
+
+ const auto ret = SHOW_metadata(node);
+
+ if (fbTrans)
+ commit_trans(&fbTrans);
+
+ return ret;
+ },
+
+ [](auto& arg)
+ {
+ static_assert(FrontendParser::AlwaysFalseV, "Add visitor method for that node type");
}
- break;
-
- case FrontOptions::edit:
- ret = edit(lparms);
- break;
-
- case FrontOptions::input:
- // CVC: Set by newinput() below only if successful.
- //Input_file = true;
- ret = newinput(lparms[1]);
- break;
-
- case FrontOptions::quit:
- ret = BACKOUT;
- break;
-
- case FrontOptions::exit:
- ret = EXIT;
- break;
-
- case FrontOptions::explain:
- ret = explain(cmd + 7);
- break;
-
- case FrontOptions::help:
- ret = help(parms[1]);
- break;
-
-#ifdef DEV_BUILD
- case FrontOptions::passthrough:
- ret = passthrough(cmd + 11);
- break;
-#endif
-
- default: // Didn't match, it must be SQL
- ret = CONT;
- break;
- }
+ }, node);
// In case any default transaction was started - commit it here
if (fbTrans)
commit_trans(&fbTrans);
- // Free the frontend command
- frontend_free_parms(parms, lparms, parm_defaults);
-
if (ret == ps_ERR)
{
+ TEXT errbuf[MSG_LENGTH];
+
if (bad_dialect)
IUTILS_msg_get(CMD_ERR, errbuf, SafeArg() << bad_dialect_buf);
else
- IUTILS_msg_get(CMD_ERR, errbuf, SafeArg() << cmd);
+ IUTILS_msg_get(CMD_ERR, errbuf, SafeArg() << statement.c_str());
+
STDERROUT(errbuf);
}
@@ -5349,426 +5345,6 @@ static processing_state frontend(const TEXT* statement)
}
-static void frontend_free_parms(TEXT* parms[], TEXT* lparms[], TEXT parm_defaults[][1])
-{
- for (int j = 0; j < MAX_TERMS; j++)
- {
- if (parms[j] && parms[j] != parm_defaults[j])
- {
- ISQL_FREE(parms[j]);
- ISQL_FREE(lparms[j]);
- }
- }
-}
-
-
-static void frontend_load_parms(const TEXT* p, TEXT* parms[], TEXT* lparms[],
- TEXT parm_defaults[][1])
-{
- TEXT buffer[BUFFER_LENGTH256];
-
- for (int i = 0; i < MAX_TERMS; ++i)
- {
- if (!*p)
- {
- parms[i] = lparms[i] = parm_defaults[i];
- parm_defaults[i][0] = '\0';
- continue;
- }
-
- bool role_found = false;
- TEXT* a = buffer;
- int j = 0;
- const bool quoted = *p == DBL_QUOTE || *p == SINGLE_QUOTE;
- if (quoted)
- {
- if (i > 0 && (!strcmp(parms[i - 1], "ROLE")))
- role_found = true;
- bool delimited_done = false;
- const TEXT end_quote = *p;
- j++;
- *a++ = *p++;
- // Allow a quoted string to have embedded spaces
- // Prevent overflow
- while (*p && !delimited_done && j < BUFFER_LENGTH256 - 1)
- {
- if (*p == end_quote)
- {
- j++;
- *a++ = *p++;
- if (*p && *p == end_quote && j < BUFFER_LENGTH256 - 1)
- {
- j++; // do not skip the escape quote here
- *a++ = *p++;
- }
- else
- delimited_done = true;
- }
- else
- {
- j++;
- *a++ = *p++;
- }
- }
- *a = '\0';
- }
- else
- {
- // Prevent overflow. Do not copy the string (redundant).
- while (*p && !fb_utils::isspace(*p) && j < BUFFER_LENGTH256 - 1)
- {
- j++;
- ++p;
- }
- }
- fb_assert(!quoted || strlen(buffer) == size_t(j));
- const size_t length = quoted ? strlen(buffer) : j;
- parms[i] = (TEXT*) ISQL_ALLOC((SLONG) (length + 1));
- lparms[i] = (TEXT*) ISQL_ALLOC((SLONG) (length + 1));
- memcpy(parms[i], quoted ? buffer : p - j, length);
- parms[i][length] = 0;
- while (*p && fb_utils::isspace(*p))
- p++;
- strcpy(lparms[i], parms[i]);
- if (!role_found)
- IUTILS_make_upper(parms[i]);
- }
-}
-
-
-// ***********************
-// f r o n t e n d _ s e t
-// ***********************
-// Validates and executes the SET {option {params}} command.
-static processing_state frontend_set(const char* cmd, const char* const* parms,
- const char* const* lparms, char* const bad_dialect_buf, bool& bad_dialect)
-{
-
- class SetOptions : public OptionsBase
- {
- public:
- enum set_commands
- {
- stat, count, list, plan, planonly, explain, blobdisplay, echo, autoddl,
- autoterm, width, transaction, terminator, names, time,
- sqlda_display,
- exec_path_display,
- sql, warning, sqlCont, heading, bail,
- bulk_insert, maxrows, stmtTimeout,
- keepTranParams, perTableStats, wireStats,
- wrong
- };
- SetOptions(const optionsMap* inmap, size_t insize, int wrongval)
- : OptionsBase(inmap, insize, wrongval)
- {}
- };
-
- static const SetOptions::optionsMap options[] =
- {
- {SetOptions::stat, "STATS", 4},
- {SetOptions::count, "COUNT", 0},
- {SetOptions::list, "LIST", 0},
- {SetOptions::plan, "PLAN", 0},
- {SetOptions::planonly, "PLANONLY", 0},
- {SetOptions::explain, "EXPLAIN", 0},
- {SetOptions::blobdisplay, "BLOBDISPLAY", 4},
- {SetOptions::echo, "ECHO", 0},
- {SetOptions::autoddl, "AUTODDL", 4},
- {SetOptions::autoterm, "AUTOTERM", 5},
- {SetOptions::width, "WIDTH", 0},
- {SetOptions::transaction, "TRANSACTION", 5},
- {SetOptions::terminator, "TERMINATOR", 4},
- {SetOptions::names, "NAMES", 0},
- {SetOptions::time, "TIME", 0},
- {SetOptions::sqlda_display, "SQLDA_DISPLAY", 0},
- {SetOptions::exec_path_display, "EXEC_PATH_DISPLAY", 0},
- {SetOptions::sql, "SQL", 0},
- {SetOptions::warning, "WARNINGS", 7},
- {SetOptions::warning, "WNG", 0},
- {SetOptions::sqlCont, "GENERATOR", 0},
- {SetOptions::sqlCont, "STATISTICS", 0},
- {SetOptions::heading, "HEADING", 0},
- {SetOptions::bail, "BAIL", 0},
- {SetOptions::bulk_insert, "BULK_INSERT", 0},
- {SetOptions::maxrows, "ROWCOUNT", 0}, // legacy, compatible with FB2.5
- {SetOptions::maxrows, "MAXROWS", 0},
- {SetOptions::sqlCont, "ROLE", 0},
- {SetOptions::sqlCont, "TRUSTED", 0}, // TRUSTED ROLE, will get DSQL error other case
- {SetOptions::stmtTimeout, "LOCAL_TIMEOUT", 0},
- {SetOptions::sqlCont, "DECFLOAT", 0},
- {SetOptions::keepTranParams, "KEEP_TRAN_PARAMS", 9},
- {SetOptions::perTableStats, "PER_TABLE_STATS", 7},
- {SetOptions::wireStats, "WIRE_STATS", 4}
- };
-
- // Display current set options
- if (!*parms[1])
- return print_sets();
-
- processing_state ret = SKIP;
- const SetOptions setoptions(options, FB_NELEM(options), SetOptions::wrong);
- switch (setoptions.getCommand(parms[1]))
- {
- case SetOptions::sqlCont:
- ret = CONT;
- break;
-
- case SetOptions::stat:
- ret = do_set_command(parms[2], &setValues.Stats);
- break;
-
- case SetOptions::count:
- ret = do_set_command(parms[2], &setValues.Docount);
- break;
-
- case SetOptions::list:
- ret = do_set_command(parms[2], &setValues.List);
- break;
-
- case SetOptions::plan:
- ret = do_set_command(parms[2], &setValues.Plan);
- if (setValues.Planonly && !setValues.Plan)
- ret = do_set_command("OFF", &setValues.Planonly);
- break;
-
- case SetOptions::planonly:
- ret = do_set_command (parms[2], &setValues.Planonly);
- if (setValues.Planonly && !setValues.Plan)
- {
- // turn on plan
- ret = do_set_command ("ON", &setValues.Plan);
- }
- break;
-
- case SetOptions::explain:
- ret = do_set_command(parms[2], &setValues.ExplainPlan);
- if (setValues.ExplainPlan)
- ret = do_set_command("ON", &setValues.Plan);
- break;
-
- case SetOptions::blobdisplay:
- // No arg means turn off blob display
- if (!*parms[2] || !strcmp(parms[2], "OFF"))
- setValues.Doblob = NO_BLOBS;
- else if (!strcmp(parms[2], "ALL"))
- setValues.Doblob = ALL_BLOBS;
- else
- setValues.Doblob = atoi(parms[2]);
- break;
-
- case SetOptions::echo:
- ret = do_set_command(parms[2], &setValues.Echo);
- if (!setValues.Echo)
- ISQL_prompt("");
- break;
-
- case SetOptions::autoddl:
- ret = do_set_command(parms[2], &setValues.Autocommit);
- break;
-
- case SetOptions::autoterm:
- ret = do_set_command(parms[2], &setValues.AutoTerm);
- if (setValues.AutoTerm)
- {
- isqlGlob.Termlen = 1;
- strcpy(isqlGlob.global_Term, ";");
- }
- break;
-
- case SetOptions::width:
- ret = newsize(parms[2][0] == '"' ? lparms[2] : parms[2], parms[3]);
- break;
-
- case SetOptions::transaction:
- ret = newtrans(cmd);
- break;
-
- case SetOptions::terminator:
- {
- const TEXT* a = (*lparms[2]) ? lparms[2] : DEFTERM;
- for (size_t iter = 0; iter < sizeof(FORBIDDEN_TERM_CHARS); ++iter)
- {
- if (strchr(a, FORBIDDEN_TERM_CHARS[iter]))
- {
- TEXT msg_string[MSG_LENGTH];
- IUTILS_msg_get(INVALID_TERM_CHARS, msg_string, SafeArg() << FORBIDDEN_TERM_CHARS_DISPLAY);
- isqlGlob.printf("%s\n", msg_string);
- return ps_ERR;
- }
- }
-
- setValues.AutoTerm = false;
-
- isqlGlob.Termlen = strlen(a);
- if (isqlGlob.Termlen < MAXTERM_SIZE)
- {
- strcpy(isqlGlob.global_Term, a);
- }
- else
- {
- isqlGlob.Termlen = MAXTERM_SIZE - 1;
- fb_utils::copy_terminate(isqlGlob.global_Term, a, isqlGlob.Termlen + 1);
- }
- }
- break;
-
- case SetOptions::names:
- if (!*parms[2])
- {
- const size_t lgth = strlen(DEFCHARSET);
- if (lgth < MAXCHARSET_SIZE)
- strcpy(setValues.ISQL_charset, DEFCHARSET);
- else
- fb_utils::copy_terminate(setValues.ISQL_charset, DEFCHARSET, MAXCHARSET_SIZE);
- }
- else
- {
- const size_t lgth = strlen(parms[2]);
- if (lgth < MAXCHARSET_SIZE)
- strcpy(setValues.ISQL_charset, parms[2]);
- else
- fb_utils::copy_terminate(setValues.ISQL_charset, parms[2], MAXCHARSET_SIZE);
- }
- break;
-
- case SetOptions::time:
- if (fb_utils::stricmp(parms[2], "ZONE") == 0)
- ret = CONT; // pass SET TIME ZONE command to server
- else
- ret = do_set_command(parms[2], &setValues.Time_display);
- break;
-
- case SetOptions::sqlda_display:
- ret = do_set_command(parms[2], &setValues.Sqlda_display);
- break;
-
- case SetOptions::exec_path_display:
- ret = SKIP;
-
- if (!*parms[2])
- ret = ps_ERR;
- else if (strcmp(parms[2], "OFF") == 0)
- setValues.ExecPathDisplay[0] = 0;
- else
- {
- Firebird::Array execPath;
-
- for (int parNum = 2; parNum < MAX_TERMS - 1 && *parms[parNum]; ++parNum)
- {
- const char* param = parms[parNum];
- UCHAR code;
-
- if (strcmp(param, "BLR") == 0)
- code = isc_info_sql_exec_path_blr_text;
- else
- {
- ret = ps_ERR;
- break;
- }
-
- if (execPath.exist(code))
- {
- ret = ps_ERR;
- break;
- }
-
- execPath.push(code);
- }
-
- if (ret != ps_ERR)
- {
- if (execPath.getCount() < sizeof(setValues.ExecPathDisplay))
- {
- memcpy(setValues.ExecPathDisplay, execPath.begin(), execPath.getCount());
- setValues.ExecPathDisplay[execPath.getCount()] = 0;
- }
- else
- ret = ps_ERR;
- }
- }
-
- if (ret != ps_ERR)
- ret = execSetDebugCommand();
-
- break;
-
- case SetOptions::sql:
- if (!strcmp(parms[2], "DIALECT"))
- ret = get_dialect(parms[3], bad_dialect_buf, bad_dialect);
- else
- ret = ps_ERR;
- break;
-
- case SetOptions::warning:
- ret = do_set_command (parms[2], &setValues.Warnings);
- break;
-
- case SetOptions::heading:
- ret = do_set_command(parms[2], &setValues.Heading);
- break;
-
- case SetOptions::bail:
- ret = do_set_command(parms[2], &setValues.BailOnError);
- break;
-
- case SetOptions::bulk_insert:
- if (*parms[2])
- ret = bulk_insert_hack(cmd);
- else
- ret = ps_ERR;
- break;
-
- case SetOptions::maxrows:
- ret = newMaxRows((*lparms[2]) ? lparms[2] : "0");
- break;
-
- case SetOptions::stmtTimeout:
- {
- int val = strtol(parms[2], NULL, 10);
- if (val < 0)
- ret = ps_ERR;
- else
- {
- setValues.StmtTimeout = val;
- ret = SKIP;
- }
- }
- break;
-
- case SetOptions::keepTranParams:
- {
- const bool oldValue = setValues.KeepTranParams;
- ret = do_set_command(parms[2], &setValues.KeepTranParams);
- if ((ret != ps_ERR) && (oldValue != setValues.KeepTranParams))
- TranParams->assign(setValues.KeepTranParams ? DEFAULT_DML_TRANS_SQL : "");
- }
-
- break;
-
- case SetOptions::perTableStats:
- ret = do_set_command(parms[2], &setValues.PerTableStats);
- break;
-
- case SetOptions::wireStats:
- ret = do_set_command(parms[2], &setValues.WireStats);
- break;
-
- default:
- //{
- // TEXT msg_string[MSG_LENGTH];
- // IUTILS_msg_get(VALID_OPTIONS, msg_string);
- // isqlGlob.printf("%s\n", msg_string);
- //}
- //setoptions.showCommands(isqlGlob.Out);
- //ret = ps_ERR;
- ret = CONT; // pass unknown SET command to server as is
- break;
- }
-
- return ret;
-}
-
-
static processing_state do_set_command(const TEXT* parm, bool* global_flag)
{
/**************************************
@@ -6721,7 +6297,7 @@ static bool printUser(const char* dbName)
}
-static processing_state newdb(TEXT* dbname,
+static processing_state newdb(const TEXT* dbname,
const TEXT* usr,
const TEXT* psw,
int numbufs,
@@ -6755,14 +6331,16 @@ static processing_state newdb(TEXT* dbname,
// out. We will restore it after the disconnect. The save_database buffer
// will also be used to translate dbname to the proper character set.
- const SLONG len = static_cast(chop_at(dbname, MAXPATHLEN));
+ const SLONG len = strnlen(dbname, MAXPATHLEN - 1);
SCHAR* save_database = (SCHAR*) ISQL_ALLOC(len + 1);
if (!save_database)
return ps_ERR;
- strcpy(save_database, dbname);
+ strncpy(save_database, dbname, len);
+ save_database[len] = 0;
ISQL_disconnect_database(false);
- strcpy(dbname, save_database);
+ strcpy(isqlGlob.global_Db_name, save_database);
+ dbname = isqlGlob.global_Db_name;
ISQL_FREE(save_database);
TEXT local_psw[BUFFER_LENGTH256];
@@ -6775,12 +6353,10 @@ static processing_state newdb(TEXT* dbname,
local_usr[0] = 0;
local_sql_role[0] = 0;
- // Strip quotes if well-intentioned
-
- strip_quotes(dbname, isqlGlob.global_Db_name);
if (usr)
strcpy(local_usr, usr);
- strip_quotes(psw, local_psw);
+ if (psw)
+ strcpy(local_psw, psw);
// if local user is not specified, see if global options are
// specified - don't let a global role setting carry forward if a
@@ -6964,20 +6540,17 @@ static processing_state newinput(const TEXT* infile)
return ps_ERR;
}
- TEXT path[MAXPATHLEN];
- strip_quotes(infile, path);
-
PathName file;
- if (PathUtils::isRelative(path))
+ if (PathUtils::isRelative(infile))
{
PathName newPath, temp;
PathUtils::splitLastComponent(newPath, temp, Filelist->Ifp().fileName(false));
- PathUtils::concatPath(file, newPath, path);
+ PathUtils::concatPath(file, newPath, infile);
}
else
- file = path;
+ file = infile;
// filelist is a linked list of file pointers. We must add a node to
// the linked list before discarding the current Ifp.
@@ -6987,11 +6560,11 @@ static processing_state newinput(const TEXT* infile)
if (fp)
{
Filelist->insertIfp();
- Filelist->Ifp().init(fp, file.c_str(), path);
+ Filelist->Ifp().init(fp, file.c_str(), infile);
}
else
{
- IUTILS_msg_get(FILE_OPEN_ERR, errbuf, SafeArg() << path);
+ IUTILS_msg_get(FILE_OPEN_ERR, errbuf, SafeArg() << infile);
STDERROUT(errbuf);
return FAIL;
}
@@ -7020,9 +6593,6 @@ static processing_state newoutput(const TEXT* outfile)
if (*outfile)
{
- TEXT path[MAXPATHLEN];
- strip_quotes(outfile, path);
- outfile = path;
FILE* fp = os_utils::fopen(outfile, "a");
if (fp)
{
@@ -7071,18 +6641,7 @@ static processing_state newsize(const TEXT* colname, const TEXT* sizestr)
* Add a column name and print width to collist
*
**************************************/
- if (!*colname || (strlen(colname) >= QUOTED_NAME_SIZE))
- return ps_ERR;
-
- char buf[QUOTED_NAME_SIZE];
- if (colname[0] == DBL_QUOTE)
- {
- strcpy(buf, colname);
- IUTILS_remove_and_unescape_quotes(buf, DBL_QUOTE);
- colname = buf;
- }
-
- if (strlen(colname) > MAX_SQL_IDENTIFIER_LEN)
+ if (!*colname || strlen(colname) > MAX_SQL_IDENTIFIER_LEN)
return ps_ERR;
// If no size is given, remove the entry
@@ -7601,25 +7160,6 @@ static processing_state parse_arg(int argc, SCHAR** argv, SCHAR* tabname)
}
-// *********************
-// p a s s t h r o u g h
-// *********************
-// Execute a command directly on the server. No interpretation done in isql.
-// Use with care. Only for debug builds.
-#ifdef DEV_BUILD
-static processing_state passthrough(const char* cmd)
-{
- if (!DB)
- return FAIL;
-
- M__trans = DB->execute(fbStatus, M__trans, 0, cmd, isqlGlob.SQL_dialect, NULL, NULL, NULL, NULL);
- if (ISQL_errmsg(fbStatus))
- return ps_ERR;
-
- return SKIP;
-}
-#endif
-
static bool checkSpecial(TEXT* const p, const int length, const double value)
{
/**************************************
diff --git a/src/isql/iutils.cpp b/src/isql/iutils.cpp
index b5e3e276d1..09dc0a537f 100644
--- a/src/isql/iutils.cpp
+++ b/src/isql/iutils.cpp
@@ -37,6 +37,7 @@
#include "../common/utils_proto.h"
#include
+using namespace Firebird;
using MsgFormat::SafeArg;
@@ -148,6 +149,16 @@ void IUTILS_msg_get(USHORT number, USHORT size, TEXT* msg, const SafeArg& args)
fb_msg_format(NULL, ISQL_MSG_FAC, number, size, msg, args);
}
+
+string IUTILS_name_to_string(const MetaString& name)
+{
+ if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
+ return name.toQuotedString();
+ else
+ return name.c_str();
+}
+
+
void IUTILS_printf(FILE* fp, const char* buffer)
{
/**************************************
diff --git a/src/isql/iutils_proto.h b/src/isql/iutils_proto.h
index 4982e17c97..c61826081a 100644
--- a/src/isql/iutils_proto.h
+++ b/src/isql/iutils_proto.h
@@ -24,6 +24,7 @@
#ifndef ISQL_IUTILS_PROTO_H
#define ISQL_IUTILS_PROTO_H
+#include "../common/classes/MetaString.h"
#include "../common/classes/SafeArg.h"
#include
@@ -33,6 +34,7 @@ void IUTILS_msg_get(USHORT number, TEXT* msg,
const MsgFormat::SafeArg& args = MsgFormat::SafeArg());
void IUTILS_msg_get(USHORT number, USHORT size, TEXT* msg,
const MsgFormat::SafeArg& args = MsgFormat::SafeArg());
+Firebird::string IUTILS_name_to_string(const Firebird::MetaString& name);
void IUTILS_printf(FILE*, const char*);
void IUTILS_printf2(FILE*, const char*, ...);
void IUTILS_put_errmsg(USHORT number, const MsgFormat::SafeArg& args);
diff --git a/src/isql/show.epp b/src/isql/show.epp
index 83903e99a7..540f758a26 100644
--- a/src/isql/show.epp
+++ b/src/isql/show.epp
@@ -45,6 +45,7 @@
#include "../isql/isql.h"
#include "../jrd/intl.h"
#include "../common/intlobj_new.h"
+#include "../common/StdHelper.h"
#include "../common/classes/AlignedBuffer.h"
#include "../common/classes/ClumpletReader.h"
#include "../isql/isql_proto.h"
@@ -68,8 +69,7 @@
#include
#endif
-using Firebird::string;
-using Firebird::MetaString;
+using namespace Firebird;
using MsgFormat::SafeArg;
@@ -83,7 +83,6 @@ DATABASE DB = EXTERN COMPILETIME "yachts.lnk" RUNTIME isqlGlob.global_Db_name;
enum commentMode {cmmShow, cmmExtract};
-static void remove_delimited_double_quotes(TEXT*);
static void make_priv_string(USHORT, char*, bool);
static processing_state show_all_tables(SSHORT);
static void show_charsets(SSHORT char_set_id, SSHORT collation);
@@ -99,23 +98,23 @@ static processing_state show_dialect();
static processing_state show_domains(const SCHAR*);
static processing_state show_exceptions(const SCHAR*);
static processing_state show_filters(const SCHAR*);
-static processing_state show_functions(const SCHAR* funcname, bool quoted, bool system, const SCHAR* msg = nullptr);
+static processing_state show_functions(
+ const char* funcname, const char* packname, bool quoted, bool system, const char* msg = nullptr);
static processing_state show_func_legacy(const MetaString&);
static processing_state show_func(const MetaString&, const MetaString&);
static processing_state show_generators(const SCHAR*);
static void show_index(SCHAR*, SCHAR*, const SSHORT, const SSHORT, const SSHORT);
-static processing_state show_indices(const SCHAR* const*);
-static processing_state show_proc(const SCHAR*, bool, bool, const char* msg = nullptr);
+static processing_state show_indices(const char*);
+static processing_state show_proc(const char*, const char*, bool, bool, const char* msg = nullptr);
static processing_state show_packages(const SCHAR* package_name, bool, const SCHAR* = NULL);
static processing_state show_publications(const SCHAR* pub_name, bool, const SCHAR* = NULL);
static void show_pub_table(const SCHAR* table_name);
static processing_state show_role(const SCHAR*, bool, const char* msg = NULL);
-static processing_state show_secclass(const char* object, const char* opt);
+static processing_state show_secclass(const std::optional& object, bool detail);
static processing_state show_table(const SCHAR*, bool);
static processing_state show_trigger(const SCHAR*, bool, bool);
static processing_state show_users();
static processing_state show_users12();
-static void parse_package(const char* procname, MetaString& package, MetaString& procedure);
static processing_state show_wireStats();
const char* const spaces = " ";
@@ -2067,7 +2066,7 @@ void SHOW_print_metadata_text_blob(FILE* fp, ISC_QUAD* blobid, bool escape_squot
}
-processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd)
+processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node)
{
/**************************************
*
@@ -2078,752 +2077,720 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd)
* Functional description
* If somebody presses the show ..., come here to
* interpret the desired command.
- * Paramters:
- * cmd -- Array of words for the command
*
**************************************/
- class ShowOptions : public OptionsBase
+ static const OptionsBase::optionsMap options[] =
{
- public:
- enum show_commands
- {
- role, table, view, system, index, domain, exception,
- filter, function, generator, grant, procedure, trigger,
- check, database, comment, dependency, collation, security_class,
- users, package, publication, schema, map, wireStats,
- wrong
- };
- ShowOptions(const optionsMap* inmap, size_t insize, int wrongval)
- : OptionsBase(inmap, insize, wrongval)
- {}
+ {0, "ROLES", 4}, // ROLE
+ {0, "TABLES", 5}, // TABLE
+ {0, "VIEWS", 4}, // VIEW
+ {0, "SYSTEM", 3}, // SYS
+ {0, "INDEXES", 3}, // IND
+ {0, "INDICES", 0},
+ {0, "DOMAINS", 6}, // DOMAIN
+ {0, "EXCEPTIONS", 5}, // EXCEPTION
+ {0, "FILTERS", 6}, // FILTER
+ {0, "FUNCTIONS", 4}, // FUNCTION
+ {0, "GENERATORS", 3}, // GEN
+ {0, "SEQUENCES", 3}, // SEQ
+ {0, "GRANTS", 5}, // GRANT
+ {0, "PROCEDURES", 4}, // PROC
+ {0, "TRIGGERS", 4}, // TRIG
+ {0, "CHECKS", 5}, // CHECK
+ {0, "DB", 0},
+ {0, "DATABASE", 0},
+ {0, "COMMENTS", 7}, // COMMENT
+ {0, "DEPENDENCY", 5}, // DEPEN
+ {0, "DEPENDENCIES", 5}, // DEPEN
+ {0, "COLLATES", 7}, // COLLATE
+ {0, "COLLATIONS", 9}, // COLLATION
+ {0, "SECCLASSES", 6}, // SECCLA
+ {0, "USERS", 0},
+ {0, "PACKAGES", 4}, // PACK
+ ///{0, "SCHEMAS", 4}, // SCHE
+ {0, "MAPPINGS", 3}, // MAP
+ {0, "PUBLICATIONS", 3}, // PUB
+ {0, "WIRE_STATISTICS", 9}, // WIRE_STAT
+ {0, "WIRE_STATS", 10}
};
- static const ShowOptions::optionsMap options[] =
- {
- //{role, "ROLE"},
- {ShowOptions::role, "ROLES", 4},
- //{table, "TABLE"},
- {ShowOptions::table, "TABLES", 5},
- //{view, "VIEW"},
- {ShowOptions::view, "VIEWS", 4},
- //{system, "SYS"},
- {ShowOptions::system, "SYSTEM", 3},
- //{index, "IND"},
- {ShowOptions::index, "INDEXES", 3},
- {ShowOptions::index, "INDICES", 0},
- //{domain, "DOMAIN"},
- {ShowOptions::domain, "DOMAINS", 6},
- //{exception, "EXCEPTION"},
- {ShowOptions::exception, "EXCEPTIONS", 5},
- //{filter, "FILTER"},
- {ShowOptions::filter, "FILTERS", 6},
- //{function, "FUNCTION"},
- {ShowOptions::function, "FUNCTIONS", 4},
- //{generator, "GEN"},
- //{generator, "GENERATOR"},
- {ShowOptions::generator, "GENERATORS", 3},
- //{generator, "SEQ"},
- //{generator, "SEQUENCE"},
- {ShowOptions::generator, "SEQUENCES", 3},
- //{grant, "GRANT"},
- {ShowOptions::grant, "GRANTS", 5},
- //{procedure, "PROC"},
- //{procedure, "PROCEDURE"},
- {ShowOptions::procedure, "PROCEDURES", 4},
- //{trigger, "TRIG"},
- //{trigger, "TRIGGER"},
- {ShowOptions::trigger, "TRIGGERS", 4},
- //{check, "CHECK"},
- {ShowOptions::check, "CHECKS", 5},
- {ShowOptions::database, "DB", 0},
- {ShowOptions::database, "DATABASE", 0},
- //{comment, "COMMENT"},
- {ShowOptions::comment, "COMMENTS", 7},
- {ShowOptions::dependency, "DEPENDENCY", 5},
- {ShowOptions::dependency, "DEPENDENCIES", 5},
- {ShowOptions::collation, "COLLATES", 7},
- {ShowOptions::collation, "COLLATIONS", 9},
- {ShowOptions::security_class, "SECURITY CLASSES", 12},
- {ShowOptions::security_class, "SECCLASSES", 6},
- {ShowOptions::users, "USERS", 0},
- {ShowOptions::package, "PACKAGES", 4},
- {ShowOptions::schema, "SCHEMAS", 4},
- {ShowOptions::map, "MAPPING", 3},
- {ShowOptions::publication, "PUBLICATIONS", 3},
- {ShowOptions::wireStats, "WIRE_STATISTICS", 9},
- {ShowOptions::wireStats, "WIRE_STATS", 10}
- };
+ const OptionsBase showoptions(options, FB_NELEM(options), 0);
- const ShowOptions showoptions(options, FB_NELEM(options), ShowOptions::wrong);
-
-
- // Can't show nothing, return an error
-
- if (!cmd[1] || !*cmd[1])
+ if (!std::holds_alternative(node) &&
+ !std::holds_alternative(node))
{
- TEXT msg_string[MSG_LENGTH];
- IUTILS_msg_get(VALID_OPTIONS, msg_string);
- isqlGlob.printf("%s\n", msg_string);
- showoptions.showCommands(isqlGlob.Out);
- return ps_ERR;
- }
-
- processing_state ret = SKIP;
- // Only show version and show sql dialect work if there is no db attached
- bool handled = true;
- if ((!strcmp(cmd[1], "VERSION")) || (!strcmp(cmd[1], "VER")))
- {
- TEXT msg_string[MSG_LENGTH];
- IUTILS_msg_get(VERSION, msg_string, SafeArg() << FB_VERSION);
- isqlGlob.printf("%s%s", msg_string, NEWLINE);
- isqlGlob.printf("Server version:%s", NEWLINE);
- VersionCallback callback;
- Firebird::UtilInterfacePtr()->getFbVersion(fbStatus, DB, &callback);
- if (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)
- {
- IUTILS_msg_get(CANNOT_GET_SRV_VER, msg_string);
- STDERROUT(msg_string);
- }
- }
- else if (!strcmp(cmd[1], "SQL"))
- {
- if (!strcmp(cmd[2], "DIALECT"))
- ret = show_dialect();
- else
- ret = ps_ERR;
- }
- else
- {
- handled = false;
if (!ISQL_dbcheck())
- ret = ps_ERR;
+ return ps_ERR;
}
- if (ret == ps_ERR || handled)
- return ret;
-
- TEXT SQL_id_for_grant[BUFFER_LENGTH256];
int key = 0;
+ MetaString notFoundName;
- switch (showoptions.getCommand(cmd[1]))
- {
- case ShowOptions::role:
- if (isqlGlob.major_ods >= ODS_VERSION9)
+ const auto ret = std::visit(StdVisitOverloads{
+ [](const FrontendParser::InvalidNode&)
{
- if (*cmd[2])
+ return CONT;
+ },
+
+ [&](const FrontendParser::ShowNode& node)
+ {
+ TEXT msg_string[MSG_LENGTH];
+ IUTILS_msg_get(VALID_OPTIONS, msg_string);
+ isqlGlob.printf("%s\n", msg_string);
+ showoptions.showCommands(isqlGlob.Out);
+ return ps_ERR;
+ },
+
+ [&](const FrontendParser::ShowChecksNode& node)
+ {
+ const auto ret = show_check((node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
{
- if (*cmd[2] == '"')
+ if (node.name)
{
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_role(lcmd[2], false);
+ FOR FIRST 1 R IN RDB$RELATIONS
+ WITH R.RDB$RELATION_NAME EQ node.name->c_str()
+ {
+ key = NO_CHECKS_ON_REL;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+
+ notFoundName = node.name.value();
+ }
+
+ if (!key)
+ key = NO_TABLE;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowCollationsNode& node)
+ {
+ const auto ret = show_collations((node.name ? node.name->c_str() : ""), 0);
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_COLLATION;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_COLLATIONS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowCommentsNode&)
+ {
+ const auto ret = show_comments(cmmShow, 0);
+
+ if (ret == OBJECT_NOT_FOUND)
+ key = NO_COMMENTS;
+
+ return ret;
+ },
+
+ [](const FrontendParser::ShowDatabaseNode&)
+ {
+ show_db();
+ return SKIP;
+ },
+
+ [&](const FrontendParser::ShowDependenciesNode& node)
+ {
+ const auto ret = show_dependencies((node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
+ key = NO_DEPENDENCIES;
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowDomainsNode& node)
+ {
+ const auto ret = show_domains((node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_DOMAIN;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_DOMAINS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowExceptionsNode& node)
+ {
+ const auto ret = show_exceptions((node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_EXCEPTION;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_EXCEPTIONS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowFiltersNode& node)
+ {
+ const auto ret = show_filters((node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_FILTER;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_FILTERS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowFunctionsNode& node)
+ {
+ const auto ret = show_functions(
+ (node.name ? node.name->c_str() : ""),
+ (node.package ? node.package->c_str() : ""),
+ false, false);
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_FUNCTION;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_FUNCTIONS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowGeneratorsNode& node)
+ {
+ const auto ret = show_generators((node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_GEN;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_GENS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowGrantsNode& node)
+ {
+ processing_state ret;
+
+ if (node.name)
+ ret = SHOW_grants(node.name->c_str(), "", obj_any);
+ else
+ ret = EXTRACT_list_grants("");
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ FOR FIRST 1 R IN RDB$RELATIONS
+ WITH R.RDB$RELATION_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_REL;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+
+ if (!key)
+ {
+ FOR FIRST 1 P IN RDB$PROCEDURES
+ WITH P.RDB$PROCEDURE_NAME EQ node.name->c_str() AND
+ P.RDB$PACKAGE_NAME MISSING
+ {
+ key = NO_GRANT_ON_PROC;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ {
+ FOR FIRST 1 R IN RDB$ROLES
+ WITH R.RDB$ROLE_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_ROL;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ {
+ FOR FIRST 1 F IN RDB$FUNCTIONS
+ WITH F.RDB$FUNCTION_NAME EQ node.name->c_str() AND
+ F.RDB$PACKAGE_NAME MISSING
+ {
+ key = NO_GRANT_ON_FUN;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ {
+ FOR FIRST 1 G IN RDB$GENERATORS
+ WITH G.RDB$GENERATOR_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_GEN;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ {
+ FOR FIRST 1 E IN RDB$EXCEPTIONS
+ WITH E.RDB$EXCEPTION_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_XCP;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ {
+ FOR FIRST 1 F IN RDB$FIELDS
+ WITH F.RDB$FIELD_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_FLD;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ {
+ FOR FIRST 1 CS IN RDB$CHARACTER_SETS
+ WITH CS.RDB$CHARACTER_SET_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_CS;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ {
+ FOR FIRST 1 C IN RDB$COLLATIONS
+ WITH C.RDB$COLLATION_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_COLL;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key && isqlGlob.major_ods >= ODS_VERSION12)
+ {
+ FOR FIRST 1 P IN RDB$PACKAGES
+ WITH P.RDB$PACKAGE_NAME EQ node.name->c_str()
+ {
+ key = NO_GRANT_ON_PKG;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+ }
+
+ if (!key)
+ key = NO_OBJECT;
+
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_GRANT_ON_ANY;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowIndexesNode& node)
+ {
+ const auto ret = show_indices((node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ FOR FIRST 1 R IN RDB$RELATIONS
+ WITH R.RDB$RELATION_NAME EQ node.name->c_str()
+ {
+ key = NO_INDICES_ON_REL;
+ }
+ END_FOR
+ ON_ERROR
+ // Ignore any error
+ END_ERROR
+
+ notFoundName = node.name.value();
+
+ if (!key)
+ key = NO_REL_OR_INDEX;
+ }
+ else
+ key = NO_INDICES;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowMappingsNode& node)
+ {
+ const auto ret = SHOW_maps(false, (node.name ? node.name->c_str() : ""));
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_MAP;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_MAPS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowPackagesNode& node)
+ {
+ const auto ret = show_packages((node.name ? node.name->c_str() : ""), false);
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_PACKAGE;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_PACKAGES;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowProceduresNode& node)
+ {
+ const auto ret = show_proc(
+ (node.name ? node.name->c_str() : ""),
+ (node.package ? node.package->c_str() : ""),
+ false, false);
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_PROC;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_PROCS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowPublicationsNode& node)
+ {
+ const auto ret = show_publications((node.name ? node.name->c_str() : ""), false);
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name)
+ {
+ key = NO_PUBLICATION;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_PUBLICATIONS;
+ }
+
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowRolesNode& node)
+ {
+ processing_state ret;
+
+ if (isqlGlob.major_ods >= ODS_VERSION9)
+ {
+ if (node.name)
+ {
+ ret = show_role(node.name->c_str(), false);
+
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ key = NO_ROLE;
+ notFoundName = node.name.value();
+ }
}
else
{
- ret = show_role(cmd[2], false);
+ ret = show_role(nullptr, false);
+ if (ret == OBJECT_NOT_FOUND)
+ key = NO_ROLES;
}
-
- if (ret == OBJECT_NOT_FOUND)
- key = NO_ROLE;
}
else
{
- ret = show_role(NULL, false);
- if (ret == OBJECT_NOT_FOUND)
- key = NO_ROLES;
+ ret = OBJECT_NOT_FOUND;
+ key = NO_ROLES;
}
- }
- else
- {
- ret = OBJECT_NOT_FOUND;
- key = NO_ROLES;
- }
- break;
- case ShowOptions::table:
- if (*cmd[2])
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowSecClassesNode& node)
{
- if (*cmd[2] == '"')
+ const auto ret = show_secclass(node.name, node.detail);
+
+ if (ret == OBJECT_NOT_FOUND)
{
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_table(lcmd[2], false);
+ if (node.name)
+ {
+ key = NO_SECCLASS;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_DB_WIDE_SECCLASS;
+ }
+
+ return ret;
+ },
+
+ [](const FrontendParser::ShowSqlDialectNode& node)
+ {
+ return show_dialect();
+ },
+
+ [](const FrontendParser::ShowSystemNode& node)
+ {
+ if (node.objType)
+ {
+ switch (node.objType.value())
+ {
+ case obj_collation:
+ show_collations("", 1);
+ break;
+
+ case obj_udf:
+ show_functions(nullptr, nullptr, false, true);
+ break;
+
+ case obj_relation:
+ show_all_tables(1);
+ break;
+
+ case obj_sql_role:
+ show_role(nullptr, true);
+ break;
+
+ case obj_procedure:
+ show_proc(nullptr, nullptr, false, true);
+ break;
+
+ case obj_package_header:
+ show_packages(nullptr, true);
+ break;
+
+ case obj_publication:
+ show_publications(nullptr, true);
+ break;
+
+ default:
+ return ps_ERR;
+ }
}
else
{
- ret = show_table(cmd[2], false);
- }
-
- if (ret == OBJECT_NOT_FOUND)
- key = NO_TABLE;
- }
- else
- {
- ret = show_all_tables(0);
- if (ret == OBJECT_NOT_FOUND)
- key = NO_TABLES;
- }
- break;
-
- case ShowOptions::view:
- if (*cmd[2])
- {
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_table(lcmd[2], true);
- }
- else
- {
- ret = show_table(cmd[2], true);
- }
-
- if (ret == OBJECT_NOT_FOUND)
- key = NO_VIEW;
- }
- else
- {
- ret = show_all_tables(-1);
- if (ret == OBJECT_NOT_FOUND)
- key = NO_VIEWS;
- }
- break;
-
- case ShowOptions::system:
- if (*cmd[2])
- {
- switch (showoptions.getCommand(cmd[2]))
- {
- case ShowOptions::collation:
- show_collations("", 1);
- break;
-
- case ShowOptions::function:
- show_functions(nullptr, false, true);
- break;
-
- case ShowOptions::table:
+ TEXT msg[MSG_LENGTH];
+ IUTILS_msg_get(MSG_TABLES, msg);
+ isqlGlob.printf("%s%s", msg, NEWLINE);
show_all_tables(1);
- break;
-
- case ShowOptions::role:
- show_role(NULL, true);
- break;
-
- case ShowOptions::procedure:
- show_proc(NULL, false, true);
- break;
-
- case ShowOptions::package:
- show_packages(NULL, true);
- break;
-
- case ShowOptions::publication:
- show_publications(NULL, true);
- break;
-
- default:
- return ps_ERR;
+ IUTILS_msg_get(MSG_FUNCTIONS, msg);
+ show_functions(nullptr, nullptr, false, true, msg);
+ IUTILS_msg_get(MSG_PROCEDURES, msg);
+ show_proc(nullptr, nullptr, false, true, msg);
+ IUTILS_msg_get(MSG_PACKAGES, msg);
+ show_packages(nullptr, true, msg);
+ IUTILS_msg_get(MSG_COLLATIONS, msg);
+ show_collations("", 1, msg, true);
+ IUTILS_msg_get(MSG_ROLES, msg);
+ show_role(nullptr, true, msg);
+ IUTILS_msg_get(MSG_PUBLICATIONS, msg);
+ show_publications(nullptr, true, msg);
}
- }
- else
- {
- TEXT msg[MSG_LENGTH];
- IUTILS_msg_get(MSG_TABLES, msg);
- isqlGlob.printf("%s%s", msg, NEWLINE);
- show_all_tables(1);
- IUTILS_msg_get(MSG_FUNCTIONS, msg);
- show_functions(nullptr, false, true, msg);
- IUTILS_msg_get(MSG_PROCEDURES, msg);
- show_proc(NULL, false, true, msg);
- IUTILS_msg_get(MSG_PACKAGES, msg);
- show_packages(NULL, true, msg);
- IUTILS_msg_get(MSG_COLLATIONS, msg);
- show_collations("", 1, msg, true);
- IUTILS_msg_get(MSG_ROLES, msg);
- show_role(NULL, true, msg);
- IUTILS_msg_get(MSG_PUBLICATIONS, msg);
- show_publications(NULL, true, msg);
- }
- break;
- case ShowOptions::index:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_indices(lcmd);
- }
- else
- {
- ret = show_indices(cmd);
- }
+ return SKIP;
+ },
- if (ret == OBJECT_NOT_FOUND)
+ [&](const FrontendParser::ShowTablesNode& node)
{
- if (*cmd[2])
+ processing_state ret;
+
+ if (node.name)
{
- FOR FIRST 1 R IN RDB$RELATIONS
- WITH R.RDB$RELATION_NAME EQ cmd[2]
+ ret = show_table(node.name->c_str(), false);
- key = NO_INDICES_ON_REL;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- if (!key)
- key = NO_REL_OR_INDEX;
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ key = NO_TABLE;
+ notFoundName = node.name.value();
+ }
}
else
{
- key = NO_INDICES;
+ ret = show_all_tables(0);
+ if (ret == OBJECT_NOT_FOUND)
+ key = NO_TABLES;
}
- }
- break;
- case ShowOptions::domain:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_domains(lcmd[2]);
- }
- else
- ret = show_domains(cmd[2]);
+ return ret;
+ },
- if (ret == OBJECT_NOT_FOUND)
+ [&](const FrontendParser::ShowTriggersNode& node)
{
- if (*cmd[2])
- key = NO_DOMAIN;
- else
- key = NO_DOMAINS;
- }
- break;
+ const auto ret = show_trigger((node.name ? node.name->c_str() : ""), true, true);
- case ShowOptions::exception:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_exceptions(lcmd[2]);
- }
- else
- ret = show_exceptions(cmd[2]);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
- key = NO_EXCEPTION;
- else
- key = NO_EXCEPTIONS;
- }
- break;
-
- case ShowOptions::filter:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_filters(lcmd[2]);
- }
- else
- ret = show_filters(cmd[2]);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
- key = NO_FILTER;
- else
- key = NO_FILTERS;
- }
- break;
-
- case ShowOptions::function:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_functions(lcmd[2], true, false);
- }
- else
- ret = show_functions(cmd[2], false, false);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
- key = NO_FUNCTION;
- else
- key = NO_FUNCTIONS;
- }
- break;
-
- case ShowOptions::generator:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_generators(lcmd[2]);
- }
- else
- ret = show_generators(cmd[2]);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
- key = NO_GEN;
- else
- key = NO_GENS;
- }
- break;
-
- case ShowOptions::grant:
- if (*cmd[2])
- {
- if (*cmd[2] == '"')
+ if (ret == OBJECT_NOT_FOUND)
{
- remove_delimited_double_quotes(lcmd[2]);
- strcpy(SQL_id_for_grant, lcmd[2]);
+ if (node.name)
+ {
+ key = NO_TRIGGER;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_TRIGGERS;
}
- else
- strcpy(SQL_id_for_grant, cmd[2]);
- ret = SHOW_grants(SQL_id_for_grant, "", obj_any);
- }
- else
- {
- strcpy(SQL_id_for_grant, cmd[2]);
- ret = EXTRACT_list_grants("");
- }
- if (ret == OBJECT_NOT_FOUND)
+ return ret;
+ },
+
+ [&](const FrontendParser::ShowUsersNode& node)
{
- if (*cmd[2])
+ const auto ret = show_users();
+
+ if (ret == OBJECT_NOT_FOUND) // It seems impossible, but...
+ key = NO_CONNECTED_USERS;
+
+ return ret;
+ },
+
+ [](const FrontendParser::ShowVersionNode&)
+ {
+ TEXT msg_string[MSG_LENGTH];
+ IUTILS_msg_get(VERSION, msg_string, SafeArg() << FB_VERSION);
+ isqlGlob.printf("%s%s", msg_string, NEWLINE);
+ isqlGlob.printf("Server version:%s", NEWLINE);
+ VersionCallback callback;
+ UtilInterfacePtr()->getFbVersion(fbStatus, DB, &callback);
+ if (fbStatus->getState() & IStatus::STATE_ERRORS)
{
- FOR FIRST 1 R IN RDB$RELATIONS
- WITH R.RDB$RELATION_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_REL;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- if (!key)
- {
- FOR FIRST 1 P IN RDB$PROCEDURES
- WITH P.RDB$PROCEDURE_NAME EQ SQL_id_for_grant AND
- P.RDB$PACKAGE_NAME MISSING
-
- key = NO_GRANT_ON_PROC;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- {
- FOR FIRST 1 R IN RDB$ROLES
- WITH R.RDB$ROLE_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_ROL;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- {
- FOR FIRST 1 F IN RDB$FUNCTIONS
- WITH F.RDB$FUNCTION_NAME EQ SQL_id_for_grant AND
- F.RDB$PACKAGE_NAME MISSING
-
- key = NO_GRANT_ON_FUN;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- {
- FOR FIRST 1 G IN RDB$GENERATORS
- WITH G.RDB$GENERATOR_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_GEN;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- {
- FOR FIRST 1 E IN RDB$EXCEPTIONS
- WITH E.RDB$EXCEPTION_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_XCP;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- {
- FOR FIRST 1 F IN RDB$FIELDS
- WITH F.RDB$FIELD_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_FLD;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- {
- FOR FIRST 1 CS IN RDB$CHARACTER_SETS
- WITH CS.RDB$CHARACTER_SET_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_CS;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- {
- FOR FIRST 1 C IN RDB$COLLATIONS
- WITH C.RDB$COLLATION_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_COLL;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key && isqlGlob.major_ods >= ODS_VERSION12)
- {
- FOR FIRST 1 P IN RDB$PACKAGES
- WITH P.RDB$PACKAGE_NAME EQ SQL_id_for_grant
-
- key = NO_GRANT_ON_PKG;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- }
- if (!key)
- key = NO_OBJECT;
+ IUTILS_msg_get(CANNOT_GET_SRV_VER, msg_string);
+ STDERROUT(msg_string);
}
- else {
- key = NO_GRANT_ON_ANY;
- }
- }
- break;
- case ShowOptions::procedure:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_proc(lcmd[2], true, false);
- }
- else
- ret = show_proc(cmd[2], false, false);
+ return SKIP;
+ },
- if (ret == OBJECT_NOT_FOUND)
+ [&](const FrontendParser::ShowViewsNode& node)
{
- if (*cmd[2])
- key = NO_PROC;
- else
- key = NO_PROCS;
- }
- break;
+ processing_state ret;
- case ShowOptions::trigger:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_trigger(lcmd[2], true, true);
- }
- else
- ret = show_trigger(cmd[2], true, true);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
+ if (node.name)
{
- /*
- FOR FIRST 1 R IN RDB$RELATIONS
- WITH R.RDB$RELATION_NAME EQ cmd[2]
+ ret = show_table(node.name->c_str(), true);
- key = NO_TRIGGERS_ON_REL;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
- if (!key)
- key = NO_REL_OR_TRIGGER;
- */
- key = NO_TRIGGER;
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ key = NO_VIEW;
+ notFoundName = node.name.value();
+ }
}
else
- key = NO_TRIGGERS;
- }
- break;
-
- case ShowOptions::check:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_check(lcmd[2]);
- }
- else
- ret = show_check(cmd[2]);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
{
- FOR FIRST 1 R IN RDB$RELATIONS
- WITH R.RDB$RELATION_NAME EQ cmd[2]
-
- key = NO_CHECKS_ON_REL;
- END_FOR
- ON_ERROR
- // Ignore any error
- END_ERROR;
+ ret = show_all_tables(-1);
+ if (ret == OBJECT_NOT_FOUND)
+ key = NO_VIEWS;
}
- if (!key)
- key = NO_TABLE;
- }
- break;
- case ShowOptions::database:
- show_db();
- break;
+ return ret;
+ },
- case ShowOptions::comment:
- ret = show_comments(cmmShow, 0);
- if (ret == OBJECT_NOT_FOUND)
- key = NO_COMMENTS;
- break;
-
- case ShowOptions::collation:
- if (*cmd[2] == '"')
+ [](const FrontendParser::ShowWireStatsNode&)
{
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_collations(lcmd[2], -1);
- }
- else
- ret = show_collations(cmd[2], 0);
+ return show_wireStats();
+ },
- if (ret == OBJECT_NOT_FOUND)
+ [](auto& arg)
{
- if (*cmd[2])
- key = NO_COLLATION;
- else
- key = NO_COLLATIONS;
+ static_assert(FrontendParser::AlwaysFalseV,
+ "Add visitor method for that show node type");
}
- break;
-
- case ShowOptions::dependency:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_dependencies(lcmd[2]);
- }
- else
- ret = show_dependencies(cmd[2]);
-
- if (ret == OBJECT_NOT_FOUND)
- key = NO_DEPENDENCIES;
- break;
-
- case ShowOptions::security_class:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_secclass(lcmd[2], cmd[3]);
- }
- else
- ret = show_secclass(cmd[2], cmd[3]);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (!strcmp(cmd[2], "*"))
- key = NO_DB_WIDE_SECCLASS;
- else
- key = NO_SECCLASS;
- }
- break;
-
- case ShowOptions::users:
- ret = show_users();
- if (ret == OBJECT_NOT_FOUND) // It seems impossible, but...
- key = NO_CONNECTED_USERS;
- break;
-
- case ShowOptions::package:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_packages(lcmd[2], false);
- }
- else
- ret = show_packages(cmd[2], false);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
- key = NO_PACKAGE;
- else
- key = NO_PACKAGES;
- }
- break;
-
- case ShowOptions::map:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = SHOW_maps(false, lcmd[2]);
- }
- else
- ret = SHOW_maps(false, cmd[2]);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
- key = NO_MAP;
- else
- key = NO_MAPS;
- }
- break;
-
- case ShowOptions::publication:
- if (*cmd[2] == '"')
- {
- remove_delimited_double_quotes(lcmd[2]);
- ret = show_publications(lcmd[2], false);
- }
- else
- ret = show_publications(cmd[2], false);
-
- if (ret == OBJECT_NOT_FOUND)
- {
- if (*cmd[2])
- key = NO_PUBLICATION;
- else
- key = NO_PUBLICATIONS;
- }
- break;
-
- case ShowOptions::schema:
- return ps_ERR;
- break;
-
- case ShowOptions::wireStats:
- ret = show_wireStats();
- break;
-
- default:
- return ps_ERR;
- } // switch
+ }, node);
if (ret == OBJECT_NOT_FOUND)
{
TEXT key_string[MSG_LENGTH];
- if (*cmd[2] == '"')
- IUTILS_msg_get(key, key_string, SafeArg() << lcmd[2]);
- else
- IUTILS_msg_get(key, key_string, SafeArg() << cmd[2]);
+ IUTILS_msg_get(key, key_string, SafeArg() << notFoundName.c_str());
STDERROUT(key_string);
}
@@ -2831,23 +2798,6 @@ processing_state SHOW_metadata(const SCHAR* const* cmd, SCHAR** lcmd)
}
-static void remove_delimited_double_quotes(TEXT* string)
-{
-/**************************************
- *
- * r e m o v e _ d e l i m i t e d _ d o u b l e _ q u o t e s
- *
- **************************************
- *
- * Functional description
- * Remove the delimited double quotes. Blanks could be part of
- * delimited SQL identifier. Unescape embedded double quotes.
- *
- **************************************/
- IUTILS_remove_and_unescape_quotes(string, DBL_QUOTE);
-}
-
-
static void make_priv_string(USHORT flags, char* string, bool useAny)
{
/**************************************
@@ -4267,7 +4217,8 @@ static processing_state show_filters(const SCHAR* object)
}
-static processing_state show_functions(const SCHAR* funcname, bool quoted, bool system, const SCHAR* msg)
+static processing_state show_functions(
+ const char* funcname, const char* packname, bool quoted, bool system, const char* msg)
{
/**************************************
*
@@ -4352,14 +4303,11 @@ static processing_state show_functions(const SCHAR* funcname, bool quoted, bool
processing_state return_state = OBJECT_NOT_FOUND;
- MetaString package, function;
- if (quoted)
- function = funcname;
- else
- parse_package(funcname, package, function);
+ const MetaString function(funcname);
+ const MetaString package(packname);
FOR FUN IN RDB$FUNCTIONS
- WITH FUN.RDB$FUNCTION_NAME EQ function.c_str()
+ WITH FUN.RDB$FUNCTION_NAME EQ funcname
AND FUN.RDB$PACKAGE_NAME EQUIV NULLIF(package.c_str(), '')
if (!FUN.RDB$MODULE_NAME.NULL)
@@ -4838,7 +4786,7 @@ static void show_index(SCHAR* relation_name,
}
-static processing_state show_indices(const SCHAR* const* cmd)
+static processing_state show_indices(const char* name)
{
/**************************************
*
@@ -4858,8 +4806,6 @@ static processing_state show_indices(const SCHAR* const* cmd)
// The names stored in the database are all upper case
- const SCHAR* name = cmd[2];
-
if (*name)
{
FOR IDX IN RDB$INDICES WITH
@@ -5187,7 +5133,7 @@ processing_state SHOW_maps(bool extract, const SCHAR* map_name)
}
-static processing_state show_proc(const SCHAR* procname, bool quoted, bool sys, const char* msg)
+static processing_state show_proc(const char* procname, const char* packname, bool quoted, bool sys, const char* msg)
{
/**************************************
*
@@ -5282,11 +5228,9 @@ static processing_state show_proc(const SCHAR* procname, bool quoted, bool sys,
// A procedure was named, so print all the info on that procedure
bool first = true;
- MetaString package, procedure;
- if (quoted)
- procedure = procname;
- else
- parse_package(procname, package, procedure);
+
+ const MetaString procedure(procname);
+ const MetaString package(packname);
FOR PRC IN RDB$PROCEDURES WITH
PRC.RDB$PROCEDURE_NAME EQ procedure.c_str() AND
@@ -5463,23 +5407,6 @@ static processing_state show_proc(const SCHAR* procname, bool quoted, bool sys,
}
-static void parse_package(const char* procname, MetaString& package, MetaString& procedure)
-{
- for (const char* point = procname; *point; ++point)
- {
- if (*point == '.')
- {
- package.assign(procname, point - procname);
- procedure = ++point;
- return;
- }
- }
-
- package = "";
- procedure = procname;
-}
-
-
static processing_state show_publications(const SCHAR* pub_name, bool sys, const SCHAR* msg)
{
/**************************************
@@ -5783,21 +5710,15 @@ static processing_state show_role(const SCHAR* object, bool system, const char*
// Show low-level, GDML security for an object. It may be table/view or procedure.
// Using SHOW SECCLASS DET[AIL] will print the contents of the sec blob.
// Using SHOW SECCLASS * DET[AIL] will print the db-wide sec class in rdb$database.
-static processing_state show_secclass(const char* object, const char* opt)
+static processing_state show_secclass(const std::optional& object, bool detail)
{
- if (!object || !*object)
- return ps_ERR;
-
- const bool detail = opt &&
- (fb_utils::stricmp(opt, "DETAIL") == 0 ||
- fb_utils::stricmp(opt, "DET") == 0);
IsqlVar var;
memset(&var, 0, sizeof(var));
var.subType = isc_blob_acl;
int count = 0;
- if (strcmp(object, "*") == 0)
+ if (!object)
{
FOR D IN RDB$DATABASE
CROSS SC IN RDB$SECURITY_CLASSES
@@ -5822,7 +5743,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR REL IN RDB$RELATIONS
CROSS SC IN RDB$SECURITY_CLASSES
OVER RDB$SECURITY_CLASS
- WITH REL.RDB$RELATION_NAME EQ object
+ WITH REL.RDB$RELATION_NAME EQ object->c_str()
++count;
isqlGlob.printf("%s's main sec class %s%s",
REL.RDB$VIEW_BLR.NULL ? "Table" : "View",
@@ -5840,7 +5761,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR REL2 IN RDB$RELATIONS
CROSS SC IN RDB$SECURITY_CLASSES
- WITH REL2.RDB$RELATION_NAME EQ object
+ WITH REL2.RDB$RELATION_NAME EQ object->c_str()
AND REL2.RDB$DEFAULT_CLASS EQ SC.RDB$SECURITY_CLASS
++count;
isqlGlob.printf("%s's default sec class %s%s",
@@ -5860,7 +5781,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR RF IN RDB$RELATION_FIELDS
CROSS SC IN RDB$SECURITY_CLASSES
OVER RDB$SECURITY_CLASS
- WITH RF.RDB$RELATION_NAME EQ object
+ WITH RF.RDB$RELATION_NAME EQ object->c_str()
SORTED BY RF.RDB$FIELD_POSITION
++count;
isqlGlob.printf(" Field %s - sec class %s%s", fb_utils::exact_name(RF.RDB$FIELD_NAME),
@@ -5879,7 +5800,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR PR IN RDB$PROCEDURES
CROSS SC IN RDB$SECURITY_CLASSES
OVER RDB$SECURITY_CLASS
- WITH PR.RDB$PROCEDURE_NAME EQ object AND
+ WITH PR.RDB$PROCEDURE_NAME EQ object->c_str() AND
PR.RDB$PACKAGE_NAME MISSING
++count;
isqlGlob.printf("Procedure's sec class %s%s",
@@ -5900,7 +5821,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR FUN IN RDB$FUNCTIONS
CROSS SC IN RDB$SECURITY_CLASSES
OVER RDB$SECURITY_CLASS
- WITH FUN.RDB$FUNCTION_NAME EQ object AND
+ WITH FUN.RDB$FUNCTION_NAME EQ object->c_str() AND
FUN.RDB$PACKAGE_NAME MISSING
++count;
isqlGlob.printf("Function's sec class %s%s",
@@ -5919,7 +5840,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR PKG IN RDB$PACKAGES
CROSS SC IN RDB$SECURITY_CLASSES
OVER RDB$SECURITY_CLASS
- WITH PKG.RDB$PACKAGE_NAME EQ object
+ WITH PKG.RDB$PACKAGE_NAME EQ object->c_str()
++count;
isqlGlob.printf("Package's sec class %s%s",
fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE);
@@ -5937,7 +5858,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR GEN IN RDB$GENERATORS
CROSS SC IN RDB$SECURITY_CLASSES
OVER RDB$SECURITY_CLASS
- WITH GEN.RDB$GENERATOR_NAME EQ object
+ WITH GEN.RDB$GENERATOR_NAME EQ object->c_str()
++count;
isqlGlob.printf("Sequence's sec class %s%s",
fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE);
@@ -5955,7 +5876,7 @@ static processing_state show_secclass(const char* object, const char* opt)
FOR XCP IN RDB$EXCEPTIONS
CROSS SC IN RDB$SECURITY_CLASSES
OVER RDB$SECURITY_CLASS
- WITH XCP.RDB$EXCEPTION_NAME EQ object
+ WITH XCP.RDB$EXCEPTION_NAME EQ object->c_str()
++count;
isqlGlob.printf("Exception's sec class %s%s",
fb_utils::exact_name(SC.RDB$SECURITY_CLASS), NEWLINE);
diff --git a/src/isql/show_proto.h b/src/isql/show_proto.h
index 169131e863..bbbe704e8e 100644
--- a/src/isql/show_proto.h
+++ b/src/isql/show_proto.h
@@ -26,6 +26,7 @@
#include "../common/classes/fb_string.h"
#include
+#include "../isql/FrontendParser.h"
#include "../jrd/obj.h"
void SHOW_comments(bool force);
@@ -36,7 +37,7 @@ void SHOW_grant_roles (const SCHAR*, bool*);
void SHOW_grant_roles2 (const SCHAR*, bool*, const TEXT*, bool);
void SHOW_print_metadata_text_blob(FILE*, ISC_QUAD*, bool escape_squote = false,
bool avoid_end_in_single_line_comment = false);
-processing_state SHOW_metadata(const SCHAR* const*, SCHAR**);
+processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node);
void SHOW_read_owner();
const Firebird::string SHOW_trigger_action(SINT64);
processing_state SHOW_maps(bool extract, const SCHAR* map_name);
diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp
new file mode 100644
index 0000000000..61a571dfdb
--- /dev/null
+++ b/src/isql/tests/FrontendParserTest.cpp
@@ -0,0 +1,621 @@
+/*
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Adriano dos Santos Fernandes
+ * for the Firebird Open Source RDBMS project.
+ *
+ * Copyright (c) 2024 Adriano dos Santos Fernandes
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ *
+ */
+
+#include "firebird.h"
+#include "boost/test/unit_test.hpp"
+#include "../FrontendParser.h"
+#include
+
+using namespace Firebird;
+
+BOOST_AUTO_TEST_SUITE(ISqlSuite)
+BOOST_AUTO_TEST_SUITE(FrontendParserSuite)
+BOOST_AUTO_TEST_SUITE(FrontendParserTests)
+
+
+BOOST_AUTO_TEST_CASE(ParseCommandTest)
+{
+ const FrontendParser::Options parserOptions;
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "add", parserOptions)));
+ BOOST_TEST((std::get(FrontendParser::parse(
+ "add table1", parserOptions)).tableName == "TABLE1"));
+ BOOST_TEST((std::get(FrontendParser::parse(
+ "add \"table2\"", parserOptions)).tableName == "table2"));
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "blobdump", parserOptions)));
+
+ {
+ const auto blobDump1 = std::get(FrontendParser::parse(
+ "blobdump 1:2 /tmp/blob.txt", parserOptions));
+ BOOST_TEST(blobDump1.blobId.gds_quad_high == 1);
+ BOOST_TEST(blobDump1.blobId.gds_quad_low == 2u);
+ BOOST_TEST(blobDump1.file.value() == "/tmp/blob.txt");
+
+ const auto blobDump2 = std::get(FrontendParser::parse(
+ "blobdump 1:2 'C:\\A dir\\blob.txt'", parserOptions));
+ BOOST_TEST((blobDump2.file.value() == "C:\\A dir\\blob.txt"));
+ }
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "blobview", parserOptions)));
+
+ {
+ const auto blobView1 = std::get(FrontendParser::parse(
+ "blobview 1:2", parserOptions));
+ BOOST_TEST(blobView1.blobId.gds_quad_high == 1);
+ BOOST_TEST(blobView1.blobId.gds_quad_low == 2u);
+ BOOST_TEST(!blobView1.file);
+ }
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "connect", parserOptions)));
+
+ {
+ const auto connect1 = std::get(FrontendParser::parse(
+ "connect 'test.fdb'", parserOptions));
+ BOOST_TEST(connect1.args[0].getProcessedString() == "test.fdb");
+
+ const auto connect2 = std::get(FrontendParser::parse(
+ "connect 'test.fdb' user user", parserOptions));
+ BOOST_TEST(connect2.args.size() == 3u);
+ }
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "copy", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "copy source destination", parserOptions)));
+
+ {
+ const auto copy1 = std::get(FrontendParser::parse(
+ "copy source \"destination\" localhost:/tmp/database.fdb", parserOptions));
+ BOOST_TEST((copy1.source == "SOURCE"));
+ BOOST_TEST((copy1.destination == "destination"));
+ BOOST_TEST(copy1.database == "localhost:/tmp/database.fdb");
+ }
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "create", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "create database", parserOptions)));
+
+ {
+ const auto createDatabase1 = std::get(FrontendParser::parse(
+ "create database 'test.fdb'", parserOptions));
+ BOOST_TEST(createDatabase1.args[0].getProcessedString() == "test.fdb");
+
+ const auto createDatabase2 = std::get(FrontendParser::parse(
+ "create database 'test.fdb' user user", parserOptions));
+ BOOST_TEST(createDatabase2.args.size() == 3u);
+ }
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "drop database x", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "drop database", parserOptions)));
+
+ BOOST_TEST(!std::get(FrontendParser::parse(
+ "edit", parserOptions)).file);
+ BOOST_TEST(std::get(FrontendParser::parse(
+ "edit /tmp/file.sql", parserOptions)).file.value() == "/tmp/file.sql");
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "exit x", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "exit", parserOptions)));
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "explain", parserOptions)));
+ BOOST_TEST(std::get(FrontendParser::parse(
+ "explain select 1 from rdb$database", parserOptions)).query == "select 1 from rdb$database");
+
+ BOOST_TEST(!std::get(FrontendParser::parse(
+ "help", parserOptions)).command.has_value());
+ BOOST_TEST(std::get(FrontendParser::parse(
+ "help set", parserOptions)).command.value() == "SET");
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "help set x", parserOptions)));
+
+ BOOST_TEST(!std::get(FrontendParser::parse(
+ "?", parserOptions)).command.has_value());
+ BOOST_TEST(std::get(FrontendParser::parse(
+ "? set", parserOptions)).command.value() == "SET");
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "? set x", parserOptions)));
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "input", parserOptions)));
+ BOOST_TEST(std::get(FrontendParser::parse(
+ "input /tmp/file.sql", parserOptions)).file == "/tmp/file.sql");
+
+ BOOST_TEST(!std::get(FrontendParser::parse(
+ "output", parserOptions)).file);
+ BOOST_TEST(std::get(FrontendParser::parse(
+ "output /tmp/file.txt", parserOptions)).file.value() == "/tmp/file.txt");
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "quit", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "quit x", parserOptions)));
+
+ BOOST_TEST(!std::get(FrontendParser::parse(
+ "shell", parserOptions)).command);
+ BOOST_TEST(std::get(FrontendParser::parse(
+ "shell ls -l /tmp", parserOptions)).command.value() == "ls -l /tmp");
+}
+
+BOOST_AUTO_TEST_CASE(ParseSetTest)
+{
+ const FrontendParser::Options parserOptions;
+
+ const auto parseSet = [&](const std::string_view text) {
+ return std::get(FrontendParser::parse(text, parserOptions));
+ };
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "\"set\"", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set x", parserOptions)));
+
+ BOOST_TEST(std::holds_alternative(parseSet("set")));
+
+ BOOST_TEST(std::get(parseSet(
+ "set auto")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set auto on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set auto off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set autoddl")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set autoddl on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set autoddl off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set autoterm")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set autoterm on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set autoterm off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set bail")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set bail on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set bail off x", parserOptions)));
+
+ BOOST_TEST((std::get(parseSet(
+ "set bulk_insert insert into mytable (a, b) values (1, ?)")).statement ==
+ "insert into mytable (a, b) values (1, ?)"));
+
+ BOOST_TEST(std::get(parseSet(
+ "set blob")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set blob on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set blob off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set blobdisplay")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set blobdisplay on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set blobdisplay off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set count")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set count on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set count off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set echo")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set echo on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set echo off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set exec_path_display")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set exec_path_display blr")).arg == "BLR"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set exec_path_display off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set explain")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set explain on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set explain off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set heading")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set heading on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set heading off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set keep_tran")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set keep_tran on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set keep_tran off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set keep_tran_params")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set keep_tran_params on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set keep_tran_params off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set list")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set list on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set list off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set local_timeout")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set local_timeout 80")).arg == "80"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set local_timeout 90 x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set maxrows")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set maxrows 80")).arg == "80"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set maxrows 90 x", parserOptions)));
+
+ BOOST_TEST(!std::get(parseSet(
+ "set names")).name.has_value());
+ BOOST_TEST((std::get(parseSet(
+ "set names utf8")).name == "UTF8"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set names utf8 x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set per_tab")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set per_tab on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set per_tab off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set per_table_stats")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set per_table_stats on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set per_table_stats off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set plan")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set plan on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set plan off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set planonly")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set planonly on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set planonly off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set rowcount")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set rowcount 80")).arg == "80"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set rowcount 90 x", parserOptions)));
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set sql", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set sql dialect", parserOptions)));
+ BOOST_TEST((std::get(parseSet(
+ "set sql dialect 3")).arg == "3"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set sql dialect 3 x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set sqlda_display")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set sqlda_display on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set sqlda_display off x", parserOptions)));
+
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set sta", parserOptions)));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set sta on", parserOptions)));
+ BOOST_TEST(std::get(parseSet(
+ "set stat")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set stat on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set stat off x", parserOptions)));
+
+ BOOST_TEST(std::get(parseSet(
+ "set stats")).arg.empty());
+ BOOST_TEST((std::get(parseSet(
+ "set stats on")).arg == "ON"));
+ BOOST_TEST(std::holds_alternative(FrontendParser::parse(
+ "set stats off x", parserOptions)));
+
+ BOOST_TEST(std::get