diff --git a/doc/sql.extensions/README.sql_package.md b/doc/sql.extensions/README.sql_package.md index 3f2140b5c3..b5fa5908cf 100644 --- a/doc/sql.extensions/README.sql_package.md +++ b/doc/sql.extensions/README.sql_package.md @@ -25,12 +25,12 @@ Output parameters: - `KEY_LENGTH` type `INTEGER` - key length for the record source - `ACCESS_PATH` type `RDB$DESCRIPTION NOT NULL` - friendly plan description -``` +```sql select * from rdb$sql.explain('select * from employee where emp_no = ?'); ``` -``` +```sql select * from rdb$sql.explain(q'{ select * @@ -43,5 +43,21 @@ select * }'); ``` +## Procedure `PARSE_UNQUALIFIED_NAMES` + +`RDB$SQL.PARSE_UNQUALIFIED_NAMES` is a selectable procedure that parses a list of unqualified SQL names and returns +one row for each name. The input must follow parse rules for names and the output of unquoted names are uppercased. + +```sql +select * + from rdb$sql.parse_unqualified_names('schema1, schema2, "schema3", "schema 4", "schema ""5"""'); + +-- SCHEMA1 +-- SCHEMA2 +-- schema3 +-- "schema 4" +-- "schema "5" +``` + # Authors - Adriano dos Santos Fernandes diff --git a/src/jrd/sys-packages/SqlPackage.cpp b/src/jrd/sys-packages/SqlPackage.cpp index 14579cd858..d7d2a66bc5 100644 --- a/src/jrd/sys-packages/SqlPackage.cpp +++ b/src/jrd/sys-packages/SqlPackage.cpp @@ -42,9 +42,6 @@ IExternalResultSet* SqlPackage::explainProcedure(ThrowStatusExceptionWrapper* st } -//-------------------------------------- - - SqlPackage::ExplainResultSet::ExplainResultSet(ThrowStatusExceptionWrapper* status, IExternalContext* context, const ExplainInput::Type* in, ExplainOutput::Type* aOut) : out(aOut) @@ -171,6 +168,42 @@ FB_BOOLEAN SqlPackage::ExplainResultSet::fetch(ThrowStatusExceptionWrapper* stat //-------------------------------------- +IExternalResultSet* SqlPackage::parseUnqualifiedNamesProcedure(ThrowStatusExceptionWrapper* status, + IExternalContext* context, const ParseUnqualifiedNamesInput::Type* in, ParseUnqualifiedNamesOutput::Type* out) +{ + return FB_NEW ParseUnqualifiedNamesResultSet(status, context, in, out); +} + + +SqlPackage::ParseUnqualifiedNamesResultSet::ParseUnqualifiedNamesResultSet(ThrowStatusExceptionWrapper* status, + IExternalContext* context, const ParseUnqualifiedNamesInput::Type* in, ParseUnqualifiedNamesOutput::Type* aOut) + : out(aOut) +{ + if (!in->namesNull) + { + const string namesStr(in->names.str, in->names.length); + MetaString::parseList(namesStr, resultEntries); + } + + resultIterator = resultEntries.begin(); +} + +FB_BOOLEAN SqlPackage::ParseUnqualifiedNamesResultSet::fetch(ThrowStatusExceptionWrapper* status) +{ + if (resultIterator == resultEntries.end()) + return false; + + out->name.set(resultIterator->c_str(), resultIterator->length()); + out->nameNull = FB_FALSE; + ++resultIterator; + + return true; +} + + +//-------------------------------------- + + SqlPackage::SqlPackage(MemoryPool& pool) : SystemPackage( pool, @@ -204,6 +237,21 @@ SqlPackage::SqlPackage(MemoryPool& pool) {"ACCESS_PATH", fld_description, false} } ), + SystemProcedure( + pool, + "PARSE_UNQUALIFIED_NAMES", + SystemProcedureFactory< + ParseUnqualifiedNamesInput, ParseUnqualifiedNamesOutput, parseUnqualifiedNamesProcedure>(), + prc_selectable, + // input parameters + { + {"NAMES", fld_text_max, true} + }, + // output parameters + { + {"NAME", fld_r_name, false} + } + ), }, // functions { diff --git a/src/jrd/sys-packages/SqlPackage.h b/src/jrd/sys-packages/SqlPackage.h index a1dde4e317..264922c08c 100644 --- a/src/jrd/sys-packages/SqlPackage.h +++ b/src/jrd/sys-packages/SqlPackage.h @@ -26,6 +26,8 @@ #include "firebird.h" #include "firebird/Message.h" #include "../common/classes/array.h" +#include "../common/classes/MetaString.h" +#include "../common/classes/objects_array.h" #include "../jrd/SystemPackages.h" namespace Jrd { @@ -88,10 +90,50 @@ private: Firebird::Array::const_iterator resultIterator = nullptr; }; - //---------- - static Firebird::IExternalResultSet* explainProcedure(Firebird::ThrowStatusExceptionWrapper* status, Firebird::IExternalContext* context, const ExplainInput::Type* in, ExplainOutput::Type* out); + + //---------- + + FB_MESSAGE(ParseUnqualifiedNamesInput, Firebird::ThrowStatusExceptionWrapper, + (FB_INTL_VARCHAR(MAX_VARY_COLUMN_SIZE / METADATA_BYTES_PER_CHAR * METADATA_BYTES_PER_CHAR, CS_METADATA), names) + ); + + FB_MESSAGE(ParseUnqualifiedNamesOutput, Firebird::ThrowStatusExceptionWrapper, + (FB_INTL_VARCHAR(METADATA_IDENTIFIER_CHAR_LEN * METADATA_BYTES_PER_CHAR, CS_METADATA), name) + ); + + class ParseUnqualifiedNamesResultSet : + public + Firebird::DisposeIface< + Firebird::IExternalResultSetImpl< + ParseUnqualifiedNamesResultSet, + Firebird::ThrowStatusExceptionWrapper + > + > + { + public: + ParseUnqualifiedNamesResultSet(Firebird::ThrowStatusExceptionWrapper* status, Firebird::IExternalContext* context, + const ParseUnqualifiedNamesInput::Type* in, ParseUnqualifiedNamesOutput::Type* out); + + public: + void dispose() override + { + delete this; + } + + public: + FB_BOOLEAN fetch(Firebird::ThrowStatusExceptionWrapper* status) override; + + private: + ParseUnqualifiedNamesOutput::Type* out; + Firebird::ObjectsArray resultEntries{*getDefaultMemoryPool()}; + Firebird::ObjectsArray::const_iterator resultIterator; + }; + + static Firebird::IExternalResultSet* parseUnqualifiedNamesProcedure(Firebird::ThrowStatusExceptionWrapper* status, + Firebird::IExternalContext* context, + const ParseUnqualifiedNamesInput::Type* in, ParseUnqualifiedNamesOutput::Type* out); };