Each new procedure needs to extend the Procedure class. For a minimal dummy procedure that doesn't actually change the result set it would look like this:
class proc_dummy: public Procedure { }
In a real procedure you'd extend at least some of the member functions below:
Prototype: n/a
The class constructors prototype signature is completely up to you. The only place where objects are instantiated is your own init callback.
To initialize your derived procedure object you have to pass on the select_result pointer the init callback was called with to the base class constructor together with a flag parameter which specifies what kind of procedure you are going to implement. So a minimal constructor would look like this:
your_proc::your_proc(select_result *res)
:Procedure(res, PROC_NO_SORT)
{
}
Possible flag values are PROC_NO_SORT and PROC_GROUP. I have no real idea yet what the two flags are doing but found that for simple procedures PROC_NO_SORT seems to be the right flag to use.
See also Section 18.2, “Initialization Callback”.
Prototype: virtual bool change_columns(List<Item> &field_list);
Here you can change the structure of the result field list, for example, you can add fields to the field_list or replace the queries result fields by something completely different alltogether (like PROCEDURE ANALYSE() does).
An example that adds an INTEGER field at the end of the field list:
bool proc_rownum::change_columns(List<Item> &field_list)
{
DBUG_ENTER("proc_rownum::change_columns");
// create a new column item
row_num_column = new Item_proc_int("RowNum");
// and attach it to the column list
field_list.push_back(row_num_column);
DBUG_RETURN(0);
}
Prototype: virtual int send_row(List<Item> &fields);
This member is called for every result row in the original result set. Whatever you do here is up to you, it is important to note though that to pass on the result row to the client you have to call result->send_data() yourself.
PROCEDURE ANALYSE() for example does not send any data here, it only produces result rows after aggregating information across all result rows so its send_row() member only aggregates but doesn't send anything.
A simple example which modifies the result value for a single field in the field list before sending it on to the client:
int proc_rownum::send_row(List<Item> &field_list __attribute__((unused)))
{
DBUG_ENTER("proc_rownum::send_row");
// increment row count and set its new value in result row
row_num_column->set(++row_num);
// now send the modified results
if (result->send_data(field_list))
DBUG_RETURN(-1);
DBUG_RETURN(0);
}
Prototype: virtual void add(void);
This member function is called once for every source row for a GROUP BY query.
See also Section 18.1.5, “end_group()”.
Prototype: virtual void end_group(void);
This member function is called whenever the end of a group in a
GROUP BY is detected, it is called after the call to
add() for the last
source row in the group but before sending the actual aggregated
result row for the group with
Section 18.1.3, “send_row()”.
Prototype: virtual bool end_of_records(void);
This member function is called at the very end after all result
rows have been processed with calls to
Section 18.1.3, “send_row()”. This is where you
can send extra summary result rows as, for example, PROCEDURE
ANALYSE() does.
