diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 48ac3aa4092..56c28b7a1f5 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -1444,6 +1444,8 @@ DmlNode* RseBoolNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* RseBoolNode* node = FB_NEW_POOL(pool) RseBoolNode(pool, blrOp); node->rse = PAR_rse(tdbb, csb); + node->rse->flags |= RseNode::FLAG_SUB_QUERY; + if (blrOp == blr_any || blrOp == blr_exists) // maybe for blr_unique as well? node->rse->flags |= RseNode::FLAG_OPT_FIRST_ROWS; @@ -1580,7 +1582,7 @@ BoolExprNode* RseBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) void RseBoolNode::pass2Boolean1(thread_db* tdbb, CompilerScratch* csb) { - if (!(rse->flags & RseNode::FLAG_VARIANT)) + if (rse->isInvariant()) { nodFlags |= FLAG_INVARIANT; csb->csb_invariants.push(&impureOffset); @@ -1607,9 +1609,8 @@ void RseBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) rsb->setAnyBoolean(rse->rse_boolean, ansiAny, ansiNot); } - csb->csb_fors.add(rsb); - - subQuery = FB_NEW_POOL(*tdbb->getDefaultPool()) SubQuery(rsb, rse->rse_invariants); + subQuery = FB_NEW_POOL(*tdbb->getDefaultPool()) SubQuery(rsb, rse); + csb->csb_fors.add(subQuery); } bool RseBoolNode::execute(thread_db* tdbb, Request* request) const diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 41280d39e69..a7dbc34bc91 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -11036,6 +11036,8 @@ DmlNode* SubQueryNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* node->rse = PAR_rse(tdbb, csb); + node->rse->flags |= RseNode::FLAG_SUB_QUERY; + if (blrOp != blr_count) node->value1 = PAR_parse_value(tdbb, csb); @@ -11319,7 +11321,7 @@ ValueExprNode* SubQueryNode::pass2(thread_db* tdbb, CompilerScratch* csb) if (!rse) ERR_post(Arg::Gds(isc_wish_list)); - if (!(rse->flags & RseNode::FLAG_VARIANT)) + if (rse->isInvariant()) { nodFlags |= FLAG_INVARIANT; csb->csb_invariants.push(&impureOffset); @@ -11356,9 +11358,8 @@ ValueExprNode* SubQueryNode::pass2(thread_db* tdbb, CompilerScratch* csb) // Finish up processing of record selection expressions. RecordSource* const rsb = CMP_post_rse(tdbb, csb, rse); - csb->csb_fors.add(rsb); - - subQuery = FB_NEW_POOL(*tdbb->getDefaultPool()) SubQuery(rsb, rse->rse_invariants); + subQuery = FB_NEW_POOL(*tdbb->getDefaultPool()) SubQuery(rsb, rse); + csb->csb_fors.add(subQuery); return this; } diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 35999205a5c..251fdfe196c 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -1302,14 +1302,17 @@ DeclareCursorNode* DeclareCursorNode::pass2(thread_db* tdbb, CompilerScratch* cs ExprNode::doPass2(tdbb, csb, rse.getAddress()); ExprNode::doPass2(tdbb, csb, refs.getAddress()); + MetaName cursorName; + csb->csb_dbg_info->curIndexToName.get(cursorNumber, cursorName); + // Finish up processing of record selection expressions. RecordSource* const rsb = CMP_post_rse(tdbb, csb, rse.getObject()); - csb->csb_fors.add(rsb); - cursor = FB_NEW_POOL(*tdbb->getDefaultPool()) Cursor(csb, rsb, rse->rse_invariants, - (rse->flags & RseNode::FLAG_SCROLLABLE), true); - csb->csb_dbg_info->curIndexToName.get(cursorNumber, cursor->name); + cursor = FB_NEW_POOL(*tdbb->getDefaultPool()) + Cursor(csb, rsb, rse, true, line, column, cursorName); + + csb->csb_fors.add(cursor); StreamList cursorStreams; cursor->getAccessPath()->findUsedStreams(cursorStreams); @@ -5065,15 +5068,16 @@ StmtNode* ForNode::pass2(thread_db* tdbb, CompilerScratch* csb) // Finish up processing of record selection expressions. RecordSource* const rsb = CMP_post_rse(tdbb, csb, rse.getObject()); - csb->csb_fors.add(rsb); - cursor = FB_NEW_POOL(*tdbb->getDefaultPool()) Cursor(csb, rsb, rse->rse_invariants, - (rse->flags & RseNode::FLAG_SCROLLABLE), !(marks & MARK_AVOID_COUNTERS)); + cursor = FB_NEW_POOL(*tdbb->getDefaultPool()) + Cursor(csb, rsb, rse, !(marks & MARK_AVOID_COUNTERS), line, column); // ASF: We cannot define the name of the cursor here, but this is not a problem, // as implicit cursors are always positioned in a valid record, and the name is // only used to raise isc_cursor_not_positioned. - if (rse->flags & RseNode::FLAG_WRITELOCK) + csb->csb_fors.add(cursor); + + if (rse->hasWriteLock()) withLock = true; if (marks & MARK_MERGE) diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index 1e31c8fb183..0caef73dbf2 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -547,10 +547,10 @@ void GEN_rse(DsqlCompilerScratch* dsqlScratch, RseNode* rse) for (const NestConst* const end = rse->dsqlStreams->items.end(); ptr != end; ++ptr) GEN_expr(dsqlScratch, *ptr); - if (rse->flags & RseNode::FLAG_WRITELOCK) + if (rse->hasWriteLock()) dsqlScratch->appendUChar(blr_writelock); - if (rse->flags & RseNode::FLAG_SKIP_LOCKED) + if (rse->hasSkipLocked()) dsqlScratch->appendUChar(blr_skip_locked); if (rse->dsqlFirst) diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index 5a357c25878..e9cf3b293e0 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -2177,13 +2177,13 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod parentRse->dsqlStreams = streamList = FB_NEW_POOL(pool) RecSourceListNode(pool, 1); streamList->items[0] = window; - if (rse->flags & RseNode::FLAG_WRITELOCK) + if (rse->hasWriteLock()) { parentRse->flags |= RseNode::FLAG_WRITELOCK; rse->flags &= ~RseNode::FLAG_WRITELOCK; } - if (rse->flags & RseNode::FLAG_SKIP_LOCKED) + if (rse->hasSkipLocked()) { parentRse->flags |= RseNode::FLAG_SKIP_LOCKED; rse->flags &= ~RseNode::FLAG_SKIP_LOCKED; diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 12ef7a65952..4c9b477e940 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -2951,9 +2951,7 @@ void RseNode::pass1Source(thread_db* tdbb, CompilerScratch* csb, RseNode* rse, // where we are just trying to inner join more than 2 streams. If possible, // try to flatten the tree out before we go any further. - const auto isLateral = (this->flags & RseNode::FLAG_LATERAL) != 0; - - if (!isLateral && + if (!isLateral() && rse->rse_jointype == blr_inner && rse_jointype == blr_inner && !rse_sorted && !rse_projection && @@ -3046,8 +3044,6 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr BoolExprNodeStack conjunctStack; - const auto isLateral = (this->flags & RseNode::FLAG_LATERAL) != 0; - // pass RseNode boolean only to inner substreams because join condition // should never exclude records from outer substreams if (opt->isInnerJoin() || (opt->isLeftJoin() && innerSubStream)) @@ -3060,7 +3056,7 @@ RecordSource* RseNode::compile(thread_db* tdbb, Optimizer* opt, bool innerSubStr StreamStateHolder stateHolder(csb, opt->getOuterStreams()); - if (opt->isLeftJoin() || isLateral) + if (opt->isLeftJoin() || isLateral()) { stateHolder.activate(); diff --git a/src/jrd/RecordSourceNodes.h b/src/jrd/RecordSourceNodes.h index d8f1452b932..127f08398d3 100644 --- a/src/jrd/RecordSourceNodes.h +++ b/src/jrd/RecordSourceNodes.h @@ -717,14 +717,53 @@ class WindowSourceNode final : public TypedNode { public: - static const USHORT FLAG_VARIANT = 0x01; // variant (not invariant?) - static const USHORT FLAG_SINGULAR = 0x02; // singleton select - static const USHORT FLAG_WRITELOCK = 0x04; // locked for write - static const USHORT FLAG_SCROLLABLE = 0x08; // scrollable cursor - static const USHORT FLAG_DSQL_COMPARATIVE = 0x10; // transformed from DSQL ComparativeBoolNode - static const USHORT FLAG_OPT_FIRST_ROWS = 0x20; // optimize retrieval for first rows - static const USHORT FLAG_LATERAL = 0x40; // lateral derived table - static const USHORT FLAG_SKIP_LOCKED = 0x80; // skip locked + enum : USHORT + { + FLAG_VARIANT = 0x01, // variant (not invariant?) + FLAG_SINGULAR = 0x02, // singleton select + FLAG_WRITELOCK = 0x04, // locked for write + FLAG_SCROLLABLE = 0x08, // scrollable cursor + FLAG_DSQL_COMPARATIVE = 0x10, // transformed from DSQL ComparativeBoolNode + FLAG_OPT_FIRST_ROWS = 0x20, // optimize retrieval for first rows + FLAG_LATERAL = 0x40, // lateral derived table + FLAG_SKIP_LOCKED = 0x80, // skip locked + FLAG_SUB_QUERY = 0x100 // sub-query + }; + + bool isInvariant() const + { + return (flags & FLAG_VARIANT) == 0; + } + + bool isSingular() const + { + return (flags & FLAG_SINGULAR) != 0; + } + + bool isScrollable() const + { + return (flags & FLAG_SCROLLABLE) != 0; + } + + bool isLateral() const + { + return (flags & FLAG_LATERAL) != 0; + } + + bool isSubQuery() const + { + return (flags & FLAG_SUB_QUERY) != 0; + } + + bool hasWriteLock() const + { + return (flags & FLAG_WRITELOCK) != 0; + } + + bool hasSkipLocked() const + { + return (flags & FLAG_SKIP_LOCKED) != 0; + } explicit RseNode(MemoryPool& pool) : TypedNode(pool), diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 0e53d3972f2..d503b47b1f7 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -36,6 +36,7 @@ #include "../jrd/met_proto.h" #include "../jrd/scl_proto.h" #include "../jrd/Collation.h" +#include "../jrd/recsrc/Cursor.h" using namespace Firebird; using namespace Jrd; @@ -723,11 +724,8 @@ string Statement::getPlan(thread_db* tdbb, bool detailed) const { string plan; - for (const auto rsb : fors) - { - plan += detailed ? "\nSelect Expression" : "\nPLAN "; - rsb->print(tdbb, plan, detailed, 0, true); - } + for (const auto select : fors) + select->printPlan(tdbb, plan, detailed); return plan; } diff --git a/src/jrd/Statement.h b/src/jrd/Statement.h index c4f679c6af0..e046537778c 100644 --- a/src/jrd/Statement.h +++ b/src/jrd/Statement.h @@ -105,7 +105,7 @@ class Statement : public pool_alloc Statement* parentStatement; // Sub routine's parent statement Firebird::Array subStatements; // Array of subroutines' statements const StmtNode* topNode; // top of execution tree - Firebird::Array fors; // record sources + Firebird::Array fors; // select expressions Firebird::Array localTables; // local tables Firebird::Array invariants; // pointer to nodes invariant offsets Firebird::RefStrPtr sqlText; // SQL text (encoded in the metadata charset) diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 2e8d8c25e93..0ca4ef8692e 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -947,25 +947,24 @@ void EXE_unwind(thread_db* tdbb, Request* request) { const Statement* statement = request->getStatement(); - if (statement->fors.getCount() || request->req_ext_resultset || request->req_ext_stmt) + if (statement->fors.hasData() || request->req_ext_resultset || request->req_ext_stmt) { Jrd::ContextPoolHolder context(tdbb, request->req_pool); Request* old_request = tdbb->getRequest(); jrd_tra* old_transaction = tdbb->getTransaction(); - try { + + try + { tdbb->setRequest(request); tdbb->setTransaction(request->req_transaction); - for (const RecordSource* const* ptr = statement->fors.begin(); - ptr != statement->fors.end(); ++ptr) - { - (*ptr)->close(tdbb); - } + for (const auto select : statement->fors) + select->close(tdbb); if (request->req_ext_resultset) { delete request->req_ext_resultset; - request->req_ext_resultset = NULL; + request->req_ext_resultset = nullptr; } while (request->req_ext_stmt) diff --git a/src/jrd/exe.h b/src/jrd/exe.h index 4db8afa0cf0..5bd3114b6d8 100644 --- a/src/jrd/exe.h +++ b/src/jrd/exe.h @@ -83,6 +83,7 @@ class DeclareVariableNode; class MessageNode; class PlanNode; class RecordSource; +class Select; // Direction for each column in sort order enum SortDirection { ORDER_ANY, ORDER_ASC, ORDER_DESC }; @@ -551,7 +552,7 @@ class CompilerScratch : public pool_alloc vec* csb_variables; // Vector of variables, if any ResourceList csb_resources; // Resources (relations and indexes) Firebird::Array csb_dependencies; // objects that this statement depends upon - Firebird::Array csb_fors; // record sources + Firebird::Array csb_fors; // select expressions Firebird::Array csb_localTables; // local tables Firebird::Array csb_invariants; // stack of pointer to nodes invariant offsets Firebird::Array csb_current_nodes; // RseNode's and other invariant diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 5c72b4273c0..cea1218ade3 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -1034,7 +1034,7 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) if (rse->rse_skip) rsb = FB_NEW_POOL(getPool()) SkipRowsStream(csb, rsb, rse->rse_skip); - if (rse->flags & RseNode::FLAG_WRITELOCK) + if (rse->hasWriteLock()) { for (const auto compileStream : compileStreams) { @@ -1048,16 +1048,16 @@ RecordSource* Optimizer::compile(BoolExprNodeStack* parentStack) SCL_update, obj_relations, tail->csb_relation->rel_name); } - rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb, (rse->flags & RseNode::FLAG_SKIP_LOCKED)); + rsb = FB_NEW_POOL(getPool()) LockedStream(csb, rsb, rse->hasSkipLocked()); } if (rse->rse_first) rsb = FB_NEW_POOL(getPool()) FirstRowsStream(csb, rsb, rse->rse_first); - if (rse->flags & RseNode::FLAG_SINGULAR) + if (rse->isSingular()) rsb = FB_NEW_POOL(getPool()) SingularStream(csb, rsb); - if (rse->flags & RseNode::FLAG_SCROLLABLE) + if (rse->isScrollable()) rsb = FB_NEW_POOL(getPool()) BufferedStream(csb, rsb); return rsb; diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 22afef52f26..5696c2ad2c9 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -1404,7 +1404,7 @@ RseNode* PAR_rse(thread_db* tdbb, CompilerScratch* csb, SSHORT rse_op) break; case blr_skip_locked: - if (!(rse->flags & RseNode::FLAG_WRITELOCK)) + if (!rse->hasWriteLock()) { PAR_error(csb, Arg::Gds(isc_random) << diff --git a/src/jrd/recsrc/Cursor.cpp b/src/jrd/recsrc/Cursor.cpp index 30300789295..05d702aa872 100644 --- a/src/jrd/recsrc/Cursor.cpp +++ b/src/jrd/recsrc/Cursor.cpp @@ -46,34 +46,82 @@ namespace return true; } +} + +// --------------------- +// Select implementation +// --------------------- - // Initialize dependent invariants - void initializeInvariants(Request* request, const VarInvariantArray* invariants) +void Select::initializeInvariants(Request* request) const +{ + // Initialize dependent invariants, if any + + if (m_rse->rse_invariants) { - if (invariants) + for (const auto offset : *m_rse->rse_invariants) { - for (const ULONG* iter = invariants->begin(); iter < invariants->end(); ++iter) - { - impure_value* const invariantImpure = request->getImpure(*iter); - invariantImpure->vlu_flags = 0; - } + const auto invariantImpure = request->getImpure(offset); + invariantImpure->vlu_flags = 0; } } } +void Select::printPlan(thread_db* tdbb, string& plan, bool detailed) const +{ + if (detailed) + { + if (m_rse->isSubQuery()) + { + plan += "\nSub-query"; + + if (m_rse->isInvariant()) + plan += " (invariant)"; + } + else if (m_cursorName.hasData()) + { + plan += "\nCursor \"" + string(m_cursorName) + "\""; + + if (m_rse->isScrollable()) + plan += " (scrollable)"; + } + else + plan += "\nSelect Expression"; + + if (m_line || m_column) + { + string pos; + pos.printf(" (line %u, column %u)", m_line, m_column); + plan += pos; + } + } + else + { + if (m_line || m_column) + { + string pos; + pos.printf("\n-- line %u, column %u", m_line, m_column); + plan += pos; + } + + plan += "\nPLAN "; + } + + m_top->print(tdbb, plan, detailed, 0, true); +} + // --------------------- // SubQuery implementation // --------------------- -SubQuery::SubQuery(const RecordSource* rsb, const VarInvariantArray* invariants) - : m_top(rsb), m_invariants(invariants) +SubQuery::SubQuery(const RecordSource* rsb, const RseNode* rse) + : Select(rsb, rse) { fb_assert(m_top); } void SubQuery::open(thread_db* tdbb) const { - initializeInvariants(tdbb->getRequest(), m_invariants); + initializeInvariants(tdbb->getRequest()); m_top->open(tdbb); } @@ -95,9 +143,10 @@ bool SubQuery::fetch(thread_db* tdbb) const // Cursor implementation // --------------------- -Cursor::Cursor(CompilerScratch* csb, const RecordSource* rsb, - const VarInvariantArray* invariants, bool scrollable, bool updateCounters) - : m_top(rsb), m_invariants(invariants), m_scrollable(scrollable), m_updateCounters(updateCounters) +Cursor::Cursor(CompilerScratch* csb, const RecordSource* rsb, const RseNode* rse, + bool updateCounters, ULONG line, ULONG column, const MetaName& name) + : Select(rsb, rse, line, column, name), + m_updateCounters(updateCounters) { fb_assert(m_top); @@ -112,7 +161,7 @@ void Cursor::open(thread_db* tdbb) const impure->irsb_active = true; impure->irsb_state = BOS; - initializeInvariants(request, m_invariants); + initializeInvariants(request); m_top->open(tdbb); } @@ -130,7 +179,7 @@ void Cursor::close(thread_db* tdbb) const bool Cursor::fetchNext(thread_db* tdbb) const { - if (m_scrollable) + if (m_rse->isScrollable()) return fetchRelative(tdbb, 1); if (!validate(tdbb)) @@ -166,7 +215,7 @@ bool Cursor::fetchNext(thread_db* tdbb) const bool Cursor::fetchPrior(thread_db* tdbb) const { - if (!m_scrollable) + if (!m_rse->isScrollable()) { // error: invalid fetch direction status_exception::raise(Arg::Gds(isc_invalid_fetch_option) << Arg::Str("PRIOR")); @@ -177,7 +226,7 @@ bool Cursor::fetchPrior(thread_db* tdbb) const bool Cursor::fetchFirst(thread_db* tdbb) const { - if (!m_scrollable) + if (!m_rse->isScrollable()) { // error: invalid fetch direction status_exception::raise(Arg::Gds(isc_invalid_fetch_option) << Arg::Str("FIRST")); @@ -188,7 +237,7 @@ bool Cursor::fetchFirst(thread_db* tdbb) const bool Cursor::fetchLast(thread_db* tdbb) const { - if (!m_scrollable) + if (!m_rse->isScrollable()) { // error: invalid fetch direction status_exception::raise(Arg::Gds(isc_invalid_fetch_option) << Arg::Str("LAST")); @@ -199,7 +248,7 @@ bool Cursor::fetchLast(thread_db* tdbb) const bool Cursor::fetchAbsolute(thread_db* tdbb, SINT64 offset) const { - if (!m_scrollable) + if (!m_rse->isScrollable()) { // error: invalid fetch direction status_exception::raise(Arg::Gds(isc_invalid_fetch_option) << Arg::Str("ABSOLUTE")); @@ -260,7 +309,7 @@ bool Cursor::fetchAbsolute(thread_db* tdbb, SINT64 offset) const bool Cursor::fetchRelative(thread_db* tdbb, SINT64 offset) const { - if (!m_scrollable) + if (!m_rse->isScrollable()) { // error: invalid fetch direction status_exception::raise(Arg::Gds(isc_invalid_fetch_option) << Arg::Str("RELATIVE")); @@ -350,6 +399,6 @@ void Cursor::checkState(Request* request) const { status_exception::raise( Arg::Gds(isc_cursor_not_positioned) << - Arg::Str(name)); + Arg::Str(m_cursorName)); } } diff --git a/src/jrd/recsrc/Cursor.h b/src/jrd/recsrc/Cursor.h index 0e96439beec..8145e479c12 100644 --- a/src/jrd/recsrc/Cursor.h +++ b/src/jrd/recsrc/Cursor.h @@ -31,26 +31,64 @@ namespace Jrd class thread_db; class CompilerScratch; class RecordSource; + class RseNode; - // SubQuery class (simplified forward-only cursor) + // Select class (common base for sub-queries and cursors) - class SubQuery + class Select { public: - SubQuery(const RecordSource* rsb, const VarInvariantArray* invariants); + enum : ULONG { + SUB_QUERY = 1, + INVARIANT = 2 + }; - void open(thread_db* tdbb) const; - void close(thread_db* tdbb) const; - bool fetch(thread_db* tdbb) const; + Select(const RecordSource* source, const RseNode* rse, + ULONG line = 0, ULONG column = 0, const MetaName& cursorName = "") + : m_top(source), m_rse(rse), m_cursorName(cursorName), + m_line(line), m_column(column) + {} - private: + virtual ~Select() + {} + + const RecordSource* getAccessPath() const + { + return m_top; + } + + void initializeInvariants(Request* request) const; + void printPlan(thread_db* tdbb, Firebird::string& plan, bool detailed) const; + + virtual void open(thread_db* tdbb) const = 0; + virtual void close(thread_db* tdbb) const = 0; + + protected: const RecordSource* const m_top; - const VarInvariantArray* const m_invariants; + const RseNode* const m_rse; + MetaName m_cursorName; // optional name for explicit PSQL cursors + + private: + ULONG m_line = 0; + ULONG m_column = 0; + }; + + // SubQuery class (simplified forward-only cursor) + + class SubQuery final : public Select + { + public: + SubQuery(const RecordSource* rsb, const RseNode* rse); + + void open(thread_db* tdbb) const override; + void close(thread_db* tdbb) const override; + + bool fetch(thread_db* tdbb) const; }; // Cursor class (wrapper around the whole access tree) - class Cursor + class Cursor final : public Select { enum State { BOS, POSITIONED, EOS }; @@ -62,11 +100,11 @@ namespace Jrd }; public: - Cursor(CompilerScratch* csb, const RecordSource* rsb, const VarInvariantArray* invariants, - bool scrollable, bool updateCounters); + Cursor(CompilerScratch* csb, const RecordSource* rsb, const RseNode* rse, + bool updateCounters, ULONG line, ULONG column, const MetaName& name = ""); - void open(thread_db* tdbb) const; - void close(thread_db* tdbb) const; + void open(thread_db* tdbb) const override; + void close(thread_db* tdbb) const override; bool fetchNext(thread_db* tdbb) const; bool fetchPrior(thread_db* tdbb) const; @@ -77,11 +115,6 @@ namespace Jrd void checkState(Request* request) const; - const RecordSource* getAccessPath() const - { - return m_top; - } - #if (!defined __GNUC__) || (__GNUC__ > 6) constexpr #endif @@ -90,14 +123,13 @@ namespace Jrd return m_updateCounters; } - public: - MetaName name; // optional name for explicit PSQL cursors + const MetaName& getName() const + { + return m_cursorName; + } private: ULONG m_impure; - const RecordSource* const m_top; - const VarInvariantArray* const m_invariants; - const bool m_scrollable; const bool m_updateCounters; };