Commit a0553ca9 authored by osku's avatar osku

Support user-function callbacks for processing results of FETCH statements

in InnoDB's SQL parser.
parent c56a927e
......@@ -1229,7 +1229,7 @@ dict_create_or_check_foreign_constraint_tables(void)
"COMMIT WORK;\n"
"END;\n";
graph = pars_sql(str);
graph = pars_sql(NULL, str);
ut_a(graph);
......@@ -1403,7 +1403,7 @@ dict_create_add_foreigns_to_dictionary(
ut_a(sqlend == sql + len + 1);
graph = pars_sql(sql);
graph = pars_sql(NULL, sql);
ut_a(graph);
......
......@@ -116,7 +116,8 @@
PARS_WORK_TOKEN = 342,
PARS_UNSIGNED_TOKEN = 343,
PARS_EXIT_TOKEN = 344,
NEG = 345
PARS_FUNCTION_TOKEN = 345,
NEG = 346
};
#endif
#define PARS_INT_LIT 258
......@@ -206,7 +207,8 @@
#define PARS_WORK_TOKEN 342
#define PARS_UNSIGNED_TOKEN 343
#define PARS_EXIT_TOKEN 344
#define NEG 345
#define PARS_FUNCTION_TOKEN 345
#define NEG 346
......
......@@ -77,6 +77,7 @@ que_t*
pars_sql(
/*=====*/
/* out, own: the query graph */
pars_info_t* info, /* in: extra information, or NULL */
const char* str); /* in: SQL string */
/*****************************************************************
Retrieves characters to the lexical analyzer. */
......@@ -157,6 +158,15 @@ pars_cursor_declaration(
table */
sel_node_t* select_node); /* in: select node */
/*************************************************************************
Parses a function declaration. */
que_node_t*
pars_function_declaration(
/*======================*/
/* out: sym_node */
sym_node_t* sym_node); /* in: function id node in the symbol
table */
/*************************************************************************
Parses a select statement. */
sel_node_t*
......@@ -301,14 +311,16 @@ pars_assignment_statement(
sym_node_t* var, /* in: variable to assign */
que_node_t* val); /* in: value to assign */
/*************************************************************************
Parses a fetch statement. */
Parses a fetch statement. into_list or user_func (but not both) must be
non-NULL. */
fetch_node_t*
pars_fetch_statement(
/*=================*/
/* out: fetch statement node */
sym_node_t* cursor, /* in: cursor node */
sym_node_t* into_list); /* in: variables to set */
sym_node_t* into_list, /* in: variables to set, or NULL */
sym_node_t* user_func); /* in: user function name, or NULL */
/*************************************************************************
Parses an open or close cursor statement. */
......@@ -427,6 +439,39 @@ pars_complete_graph_for_exec(
trx_t* trx, /* in: transaction handle */
mem_heap_t* heap); /* in: memory heap from which allocated */
/********************************************************************
Get user function with the given name.*/
pars_user_func_t*
pars_info_get_user_func(
/*====================*/
/* out: user func, or NULL if not
found */
pars_info_t* info, /* in: info struct */
const char* name); /* in: function name to find*/
/* Extra information (possibly) supplied for pars_sql(). */
struct pars_info_struct {
pars_user_func_t* funcs; /* User functions, owned by
the user, who's responsible
for freeing them as
necessary. */
ulint n_funcs; /* number of user functions */
};
/* Type of the user functions. The first argument is always InnoDB-supplied
and varies in type, while 'user_arg' is a user-supplied argument. The
meaning of the return type also varies. See the individual use cases, e.g.
the FETCH statement, for details on them. */
typedef void* (*pars_user_func_cb_t)(void* arg, void* user_arg);
/* User-supplied function and argument. */
struct pars_user_func_struct {
const char* name; /* function name */
pars_user_func_cb_t func; /* function address */
void* arg; /* user-supplied argument */
};
/* Struct used to denote a reserved word in a parsing tree */
struct pars_res_word_struct{
......
......@@ -158,6 +158,7 @@ struct sym_tab_struct{
/* position of the next character in
sql_string to give to the lexical
analyzer */
pars_info_t* info; /* extra information, or NULL */
sym_node_list_t sym_list;
/* list of symbol nodes in the symbol
table */
......@@ -180,6 +181,7 @@ struct sym_tab_struct{
#define SYM_CURSOR 96 /* named cursor */
#define SYM_PROCEDURE_NAME 97 /* stored procedure name */
#define SYM_INDEX 98 /* database index name */
#define SYM_FUNCTION 99 /* user function name */
#ifndef UNIV_NONINL
#include "pars0sym.ic"
......
......@@ -9,6 +9,8 @@ Created 1/11/1998 Heikki Tuuri
#ifndef pars0types_h
#define pars0types_h
typedef struct pars_info_struct pars_info_t;
typedef struct pars_user_func_struct pars_user_func_t;
typedef struct sym_node_struct sym_node_t;
typedef struct sym_tab_struct sym_tab_t;
typedef struct pars_res_word_struct pars_res_word_t;
......
......@@ -78,6 +78,15 @@ fetch_step(
/*=======*/
/* out: query thread to run next or NULL */
que_thr_t* thr); /* in: query thread */
/********************************************************************
Sample callback function for fetch that prints each row.*/
void*
row_fetch_print(
/*============*/
/* out: always returns non-NULL */
void* row, /* in: sel_node_t* */
void* user_arg); /* in: not used */
/***************************************************************
Prints a row in a select result. */
......@@ -311,6 +320,20 @@ struct fetch_node_struct{
que_common_t common; /* type: QUE_NODE_FETCH */
sel_node_t* cursor_def; /* cursor definition */
sym_node_t* into_list; /* variables to set */
pars_user_func_t*
func; /* User callback function or NULL.
The first argument to the function
is a sel_node_t*, containing the
results of the SELECT operation for
one row. If the function returns
NULL, it is not interested in
further rows and the cursor is
modified so (cursor % NOTFOUND) is
true. If it returns not-NULL,
continue normally. See
row_fetch_print() for an example
(and a useful debugging tool). */
};
/* Open or close cursor statement node */
......
This diff is collapsed.
This diff is collapsed.
......@@ -116,7 +116,8 @@
PARS_WORK_TOKEN = 342,
PARS_UNSIGNED_TOKEN = 343,
PARS_EXIT_TOKEN = 344,
NEG = 345
PARS_FUNCTION_TOKEN = 345,
NEG = 346
};
#endif
#define PARS_INT_LIT 258
......@@ -206,7 +207,8 @@
#define PARS_WORK_TOKEN 342
#define PARS_UNSIGNED_TOKEN 343
#define PARS_EXIT_TOKEN 344
#define NEG 345
#define PARS_FUNCTION_TOKEN 345
#define NEG 346
......
......@@ -117,6 +117,7 @@ yylex(void);
%token PARS_WORK_TOKEN
%token PARS_UNSIGNED_TOKEN
%token PARS_EXIT_TOKEN
%token PARS_FUNCTION_TOKEN
%left PARS_AND_TOKEN PARS_OR_TOKEN
%left PARS_NOT_TOKEN
......@@ -228,6 +229,10 @@ predefined_procedure_name:
| PARS_ASSERT_TOKEN { $$ = &pars_assert_token; }
;
user_function_call:
PARS_ID_TOKEN '(' ')' { $$ = $1; }
;
table_list:
PARS_ID_TOKEN { $$ = que_node_list_add_last(NULL, $1); }
| table_list ',' PARS_ID_TOKEN
......@@ -453,7 +458,9 @@ close_cursor_statement:
fetch_statement:
PARS_FETCH_TOKEN PARS_ID_TOKEN PARS_INTO_TOKEN variable_list
{ $$ = pars_fetch_statement($2, $4); }
{ $$ = pars_fetch_statement($2, $4, NULL); }
| PARS_FETCH_TOKEN PARS_ID_TOKEN PARS_INTO_TOKEN user_function_call
{ $$ = pars_fetch_statement($2, NULL, $4); }
;
column_def:
......@@ -575,10 +582,20 @@ cursor_declaration:
{ $$ = pars_cursor_declaration($3, $5); }
;
function_declaration:
PARS_DECLARE_TOKEN PARS_FUNCTION_TOKEN PARS_ID_TOKEN ';'
{ $$ = pars_function_declaration($3); }
;
declaration:
cursor_declaration
| function_declaration
;
declaration_list:
/* Nothing */
| cursor_declaration
| declaration_list cursor_declaration
| declaration
| declaration_list declaration
;
procedure_definition:
......
......@@ -494,6 +494,10 @@ In the state 'id', only two actions are possible (defined below). */
return(PARS_EXIT_TOKEN);
}
"FUNCTION" {
return(PARS_FUNCTION_TOKEN);
}
{ID} {
yylval = sym_tab_add_id(pars_sym_tab_global,
(byte*)yytext,
......
......@@ -373,14 +373,15 @@ pars_resolve_exp_variables_and_types(
}
/* Not resolved yet: look in the symbol table for a variable
or a cursor with the same name */
or a cursor or a function with the same name */
node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list);
while (node) {
if (node->resolved
&& ((node->token_type == SYM_VAR)
|| (node->token_type == SYM_CURSOR))
|| (node->token_type == SYM_CURSOR)
|| (node->token_type == SYM_FUNCTION))
&& node->name
&& (sym_node->name_len == node->name_len)
&& (ut_memcmp(sym_node->name, node->name,
......@@ -786,6 +787,26 @@ pars_cursor_declaration(
return(sym_node);
}
/*************************************************************************
Parses a function declaration. */
que_node_t*
pars_function_declaration(
/*======================*/
/* out: sym_node */
sym_node_t* sym_node) /* in: function id node in the symbol
table */
{
sym_node->resolved = TRUE;
sym_node->token_type = SYM_FUNCTION;
/* Check that the function exists. */
ut_a(pars_info_get_user_func(pars_sym_tab_global->info,
sym_node->name));
return(sym_node);
}
/*************************************************************************
Parses a delete or update statement start. */
......@@ -1433,26 +1454,42 @@ pars_procedure_call(
}
/*************************************************************************
Parses a fetch statement. */
Parses a fetch statement. into_list or user_func (but not both) must be
non-NULL. */
fetch_node_t*
pars_fetch_statement(
/*=================*/
/* out: fetch statement node */
sym_node_t* cursor, /* in: cursor node */
sym_node_t* into_list) /* in: variables to set */
sym_node_t* into_list, /* in: variables to set, or NULL */
sym_node_t* user_func) /* in: user function name, or NULL */
{
sym_node_t* cursor_decl;
fetch_node_t* node;
/* Logical XOR. */
ut_a(!into_list != !user_func);
node = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(fetch_node_t));
node->common.type = QUE_NODE_FETCH;
pars_resolve_exp_variables_and_types(NULL, cursor);
pars_resolve_exp_list_variables_and_types(NULL, into_list);
node->into_list = into_list;
if (into_list) {
pars_resolve_exp_list_variables_and_types(NULL, into_list);
node->into_list = into_list;
node->func = NULL;
} else {
pars_resolve_exp_variables_and_types(NULL, user_func);
node->func = pars_info_get_user_func(pars_sym_tab_global->info,
user_func->name);
ut_a(node->func);
node->into_list = NULL;
}
cursor_decl = cursor->alias;
......@@ -1460,8 +1497,11 @@ pars_fetch_statement(
node->cursor_def = cursor_decl->cursor_def;
ut_a(que_node_list_get_len(into_list)
== que_node_list_get_len(node->cursor_def->select_list));
if (into_list) {
ut_a(que_node_list_get_len(into_list)
== que_node_list_get_len(
node->cursor_def->select_list));
}
return(node);
}
......@@ -1822,6 +1862,7 @@ que_t*
pars_sql(
/*=====*/
/* out, own: the query graph */
pars_info_t* info, /* in: extra information, or NULL */
const char* str) /* in: SQL string */
{
sym_node_t* sym_node;
......@@ -1841,6 +1882,7 @@ pars_sql(
pars_sym_tab_global->sql_string = mem_heap_strdup(heap, str);
pars_sym_tab_global->string_len = strlen(str);
pars_sym_tab_global->next_char_pos = 0;
pars_sym_tab_global->info = info;
yyparse();
......@@ -1891,3 +1933,29 @@ pars_complete_graph_for_exec(
return(thr);
}
/********************************************************************
Get user function with the given name.*/
pars_user_func_t*
pars_info_get_user_func(
/*====================*/
/* out: user func, or NULL if not
found */
pars_info_t* info, /* in: info struct */
const char* name) /* in: function name to find*/
{
ulint i;
if (!info) {
return(NULL);
}
for (i = 0; i < info->n_funcs; i++) {
if (strcmp(info->funcs[i].name, name) == 0) {
return(&info->funcs[i]);
}
}
return(NULL);
}
......@@ -2510,7 +2510,7 @@ do not allow the discard. We also reserve the data dictionary latch. */
(ulong) ut_dulint_get_high(new_id),
(ulong) ut_dulint_get_low(new_id));
graph = pars_sql(buf);
graph = pars_sql(NULL, buf);
ut_a(graph);
......@@ -2942,7 +2942,7 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */
(ulong) ut_dulint_get_high(new_id),
(ulong) ut_dulint_get_low(new_id));
graph = pars_sql(sql);
graph = pars_sql(NULL, sql);
ut_a(graph);
......@@ -3166,7 +3166,7 @@ row_drop_table_for_mysql(
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */
graph = pars_sql(sql);
graph = pars_sql(NULL, sql);
ut_a(graph);
mem_free(sql);
......@@ -3781,7 +3781,7 @@ row_rename_table_for_mysql(
ut_a(sqlend == sql + len + 1);
graph = pars_sql(sql);
graph = pars_sql(NULL, sql);
ut_a(graph);
mem_free(sql);
......
......@@ -1975,7 +1975,18 @@ fetch_step(
if (sel_node->state != SEL_NODE_NO_MORE_ROWS) {
sel_assign_into_var_values(node->into_list, sel_node);
if (node->into_list) {
sel_assign_into_var_values(node->into_list,
sel_node);
} else {
void* ret = (*node->func->func)(sel_node,
node->func->arg);
if (!ret) {
sel_node->state =
SEL_NODE_NO_MORE_ROWS;
}
}
}
thr->run_node = que_node_get_parent(node);
......@@ -2004,6 +2015,46 @@ fetch_step(
return(thr);
}
/********************************************************************
Sample callback function for fetch that prints each row.*/
void*
row_fetch_print(
/*============*/
/* out: always returns non-NULL */
void* row, /* in: sel_node_t* */
void* user_arg) /* in: not used */
{
sel_node_t* node = row;
que_node_t* exp;
ulint i = 0;
UT_NOT_USED(user_arg);
fprintf(stderr, "row_fetch_print: row %p\n", row);
exp = node->select_list;
while (exp) {
dfield_t* dfield = que_node_get_val(exp);
dtype_t* type = dfield_get_type(dfield);
fprintf(stderr, " column %lu:\n", (ulong)i);
dtype_print(type);
fprintf(stderr, "\n");
ut_print_buf(stderr, dfield_get_data(dfield),
dfield_get_len(dfield));
fprintf(stderr, "\n");
exp = que_node_get_next(exp);
i++;
}
return((void*)42);
}
/***************************************************************
Prints a row in a select result. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment