mirror of
https://github.com/MariaDB/server.git
synced 2025-03-22 23:18:38 +01:00

This patch adds support for SYS_REFCURSOR (a weakly typed cursor) for both sql_mode=ORACLE and sql_mode=DEFAULT. Works as a regular stored routine variable, parameter and return value: - can be passed as an IN parameter to stored functions and procedures - can be passed as an INOUT and OUT parameter to stored procedures - can be returned from a stored function Note, strongly typed REF CURSOR will be added separately. Note, to maintain dependencies easier, some parts of sql_class.h and item.h were moved to new header files: - select_results.h: class select_result_sink class select_result class select_result_interceptor - sp_cursor.h: class sp_cursor_statistics class sp_cursor - sp_rcontext_handler.h class Sp_rcontext_handler and its descendants The implementation consists of the following parts: - A new class sp_cursor_array deriving from Dynamic_array - A new class Statement_rcontext which contains data shared between sub-statements of a compound statement. It has a member m_statement_cursors of the sp_cursor_array data type, as well as open cursor counter. THD inherits from Statement_rcontext. - A new data type handler Type_handler_sys_refcursor in plugins/type_cursor/ It is designed to store uint16 references - positions of the cursor in THD::m_statement_cursors. - Type_handler_sys_refcursor suppresses some derived numeric features. When a SYS_REFCURSOR variable is used as an integer an error is raised. - A new abstract class sp_instr_fetch_cursor. It's needed to share the common code between "OPEN cur" (for static cursors) and "OPER cur FOR stmt" (for SYS_REFCURSORs). - New sp_instr classes: * sp_instr_copen_by_ref - OPEN sys_ref_curor FOR stmt; * sp_instr_cfetch_by_ref - FETCH sys_ref_cursor INTO targets; * sp_instr_cclose_by_ref - CLOSE sys_ref_cursor; * sp_instr_destruct_variable - to destruct SYS_REFCURSOR variables when the execution goes out of the BEGIN..END block where SYS_REFCURSOR variables are declared. - New methods in LEX: * sp_open_cursor_for_stmt - handles "OPEN sys_ref_cursor FOR stmt". * sp_add_instr_fetch_cursor - "FETCH cur INTO targets" for both static cursors and SYS_REFCURSORs. * sp_close - handles "CLOSE cur" both for static cursors and SYS_REFCURSORs. - Changes in cursor functions to handle both static cursors and SYS_REFCURSORs: * Item_func_cursor_isopen * Item_func_cursor_found * Item_func_cursor_notfound * Item_func_cursor_rowcount - A new system variable @@max_open_cursors - to limit the number of cursors (static and SYS_REFCURSORs) opened at the same time. Its allowed range is [0-65536], with 50 by default. - A new virtual method Type_handler::can_return_bool() telling if calling item->val_bool() is allowed for Items of this data type, or if otherwise the "Illegal parameter for operation" error should be raised at fix_fields() time. - New methods in Sp_rcontext_handler: * get_cursor() * get_cursor_by_ref() - A new class Sp_rcontext_handler_statement to handle top level statement wide cursors which are shared by all substatements. - A new virtual method expr_event_handler() in classes Item and Field. It's needed to close (and make available for a new OPEN) unused THD::m_statement_cursors elements which do not have any references any more. It can happen in various moments in time, e.g. * after evaluation parameters of an SQL routine * after assigning a cursor expression into a SYS_REFCURSOR variable * when leaving a BEGIN..END block with SYS_REFCURSOR variables * after setting OUT/INOUT routine actual parameters from formal parameters.
184 lines
5.8 KiB
C++
184 lines
5.8 KiB
C++
/* Copyright (c) 2000, 2016, Oracle and/or its affiliates.
|
|
Copyright (c) 2009, 2025, MariaDB Corporation.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
|
|
|
|
#ifndef SELECT_RESULT_INCLUDED
|
|
#define SELECT_RESULT_INCLUDED
|
|
|
|
/* Pure interface for sending tabular data */
|
|
class select_result_sink: public Sql_alloc
|
|
{
|
|
public:
|
|
THD *thd;
|
|
select_result_sink(THD *thd_arg): thd(thd_arg) {}
|
|
int send_data_with_check(List<Item> &items,
|
|
SELECT_LEX_UNIT *u,
|
|
ha_rows sent);
|
|
/*
|
|
send_data returns 0 on ok, 1 on error and -1 if data was ignored, for
|
|
example for a duplicate row entry written to a temp table.
|
|
*/
|
|
virtual int send_data(List<Item> &items)=0;
|
|
virtual ~select_result_sink() = default;
|
|
// Used in cursors to initialize and reset
|
|
void reinit(THD *thd_arg) { thd= thd_arg; }
|
|
};
|
|
|
|
class select_result_interceptor;
|
|
|
|
/*
|
|
Interface for sending tabular data, together with some other stuff:
|
|
|
|
- Primary purpose seems to be sending typed tabular data:
|
|
= the DDL is sent with send_fields()
|
|
= the rows are sent with send_data()
|
|
Besides that,
|
|
- there seems to be an assumption that the sent data is a result of
|
|
SELECT_LEX_UNIT *unit,
|
|
- nest_level is used by SQL parser
|
|
*/
|
|
|
|
class select_result :public select_result_sink
|
|
{
|
|
protected:
|
|
/*
|
|
All descendant classes have their send_data() skip the first
|
|
unit->offset_limit_cnt rows sent. Select_materialize
|
|
also uses unit->get_column_types().
|
|
*/
|
|
SELECT_LEX_UNIT *unit;
|
|
/* Something used only by the parser: */
|
|
public:
|
|
ha_rows est_records; /* estimated number of records in the result */
|
|
select_result(THD *thd_arg): select_result_sink(thd_arg), est_records(0) {}
|
|
void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
|
|
virtual ~select_result() = default;
|
|
/**
|
|
Change wrapped select_result.
|
|
|
|
Replace the wrapped result object with new_result and call
|
|
prepare() and prepare2() on new_result.
|
|
|
|
This base class implementation doesn't wrap other select_results.
|
|
|
|
@param new_result The new result object to wrap around
|
|
|
|
@retval false Success
|
|
@retval true Error
|
|
*/
|
|
virtual bool change_result(select_result *new_result)
|
|
{
|
|
return false;
|
|
}
|
|
virtual int prepare(List<Item> &list, SELECT_LEX_UNIT *u)
|
|
{
|
|
unit= u;
|
|
return 0;
|
|
}
|
|
virtual int prepare2(JOIN *join) { return 0; }
|
|
/*
|
|
Because of peculiarities of prepared statements protocol
|
|
we need to know number of columns in the result set (if
|
|
there is a result set) apart from sending columns metadata.
|
|
*/
|
|
virtual uint field_count(List<Item> &fields) const
|
|
{ return fields.elements; }
|
|
virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
|
|
virtual bool initialize_tables (JOIN *join) { return 0; }
|
|
virtual bool send_eof()=0;
|
|
/**
|
|
Check if this query returns a result set and therefore is allowed in
|
|
cursors and set an error message if it is not the case.
|
|
|
|
@retval FALSE success
|
|
@retval TRUE error, an error message is set
|
|
*/
|
|
virtual bool check_simple_select() const;
|
|
virtual void abort_result_set() {}
|
|
virtual void reset_for_next_ps_execution();
|
|
void set_thd(THD *thd_arg) { thd= thd_arg; }
|
|
void reinit(THD *thd_arg)
|
|
{
|
|
select_result_sink::reinit(thd_arg);
|
|
unit= NULL;
|
|
}
|
|
#ifdef EMBEDDED_LIBRARY
|
|
virtual void begin_dataset() {}
|
|
#else
|
|
void begin_dataset() {}
|
|
#endif
|
|
virtual void update_used_tables() {}
|
|
|
|
/* this method is called just before the first row of the table can be read */
|
|
virtual void prepare_to_read_rows() {}
|
|
|
|
void remove_offset_limit()
|
|
{
|
|
unit->lim.remove_offset();
|
|
}
|
|
|
|
/*
|
|
This returns
|
|
- NULL if the class sends output row to the client
|
|
- this if the output is set elsewhere (a file, @variable, or table).
|
|
*/
|
|
virtual select_result_interceptor *result_interceptor()=0;
|
|
|
|
/*
|
|
This method is used to distinguish an normal SELECT from the cursor
|
|
structure discovery for cursor%ROWTYPE routine variables.
|
|
If this method returns "true", then a SELECT execution performs only
|
|
all preparation stages, but does not fetch any rows.
|
|
*/
|
|
virtual bool view_structure_only() const { return false; }
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
Base class for select_result descendants which intercept and
|
|
transform result set rows. As the rows are not sent to the client,
|
|
sending of result set metadata should be suppressed as well.
|
|
*/
|
|
|
|
class select_result_interceptor: public select_result
|
|
{
|
|
public:
|
|
select_result_interceptor(THD *thd_arg):
|
|
select_result(thd_arg), suppress_my_ok(false)
|
|
{
|
|
DBUG_ENTER("select_result_interceptor::select_result_interceptor");
|
|
DBUG_PRINT("enter", ("this %p", this));
|
|
DBUG_VOID_RETURN;
|
|
} /* Remove gcc warning */
|
|
uint field_count(List<Item> &fields) const override { return 0; }
|
|
bool send_result_set_metadata(List<Item> &fields, uint flag) override { return FALSE; }
|
|
select_result_interceptor *result_interceptor() override { return this; }
|
|
|
|
/*
|
|
Instruct the object to not call my_ok(). Client output will be handled
|
|
elsewhere. (this is used by ANALYZE $stmt feature).
|
|
*/
|
|
void disable_my_ok_calls() { suppress_my_ok= true; }
|
|
void reinit(THD *thd_arg)
|
|
{
|
|
select_result::reinit(thd_arg);
|
|
suppress_my_ok= false;
|
|
}
|
|
protected:
|
|
bool suppress_my_ok;
|
|
};
|
|
|
|
#endif // SELECT_RESULT_INCLUDED
|