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) ...@@ -1229,7 +1229,7 @@ dict_create_or_check_foreign_constraint_tables(void)
"COMMIT WORK;\n" "COMMIT WORK;\n"
"END;\n"; "END;\n";
graph = pars_sql(str); graph = pars_sql(NULL, str);
ut_a(graph); ut_a(graph);
...@@ -1403,7 +1403,7 @@ loop: ...@@ -1403,7 +1403,7 @@ loop:
ut_a(sqlend == sql + len + 1); ut_a(sqlend == sql + len + 1);
graph = pars_sql(sql); graph = pars_sql(NULL, sql);
ut_a(graph); ut_a(graph);
......
...@@ -116,7 +116,8 @@ ...@@ -116,7 +116,8 @@
PARS_WORK_TOKEN = 342, PARS_WORK_TOKEN = 342,
PARS_UNSIGNED_TOKEN = 343, PARS_UNSIGNED_TOKEN = 343,
PARS_EXIT_TOKEN = 344, PARS_EXIT_TOKEN = 344,
NEG = 345 PARS_FUNCTION_TOKEN = 345,
NEG = 346
}; };
#endif #endif
#define PARS_INT_LIT 258 #define PARS_INT_LIT 258
...@@ -206,7 +207,8 @@ ...@@ -206,7 +207,8 @@
#define PARS_WORK_TOKEN 342 #define PARS_WORK_TOKEN 342
#define PARS_UNSIGNED_TOKEN 343 #define PARS_UNSIGNED_TOKEN 343
#define PARS_EXIT_TOKEN 344 #define PARS_EXIT_TOKEN 344
#define NEG 345 #define PARS_FUNCTION_TOKEN 345
#define NEG 346
......
...@@ -77,6 +77,7 @@ que_t* ...@@ -77,6 +77,7 @@ que_t*
pars_sql( pars_sql(
/*=====*/ /*=====*/
/* out, own: the query graph */ /* out, own: the query graph */
pars_info_t* info, /* in: extra information, or NULL */
const char* str); /* in: SQL string */ const char* str); /* in: SQL string */
/***************************************************************** /*****************************************************************
Retrieves characters to the lexical analyzer. */ Retrieves characters to the lexical analyzer. */
...@@ -157,6 +158,15 @@ pars_cursor_declaration( ...@@ -157,6 +158,15 @@ pars_cursor_declaration(
table */ table */
sel_node_t* select_node); /* in: select node */ 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. */ Parses a select statement. */
sel_node_t* sel_node_t*
...@@ -301,14 +311,16 @@ pars_assignment_statement( ...@@ -301,14 +311,16 @@ pars_assignment_statement(
sym_node_t* var, /* in: variable to assign */ sym_node_t* var, /* in: variable to assign */
que_node_t* val); /* in: value 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* fetch_node_t*
pars_fetch_statement( pars_fetch_statement(
/*=================*/ /*=================*/
/* out: fetch statement node */ /* out: fetch statement node */
sym_node_t* cursor, /* in: cursor 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. */ Parses an open or close cursor statement. */
...@@ -427,6 +439,39 @@ pars_complete_graph_for_exec( ...@@ -427,6 +439,39 @@ pars_complete_graph_for_exec(
trx_t* trx, /* in: transaction handle */ trx_t* trx, /* in: transaction handle */
mem_heap_t* heap); /* in: memory heap from which allocated */ 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 used to denote a reserved word in a parsing tree */
struct pars_res_word_struct{ struct pars_res_word_struct{
......
...@@ -158,6 +158,7 @@ struct sym_tab_struct{ ...@@ -158,6 +158,7 @@ struct sym_tab_struct{
/* position of the next character in /* position of the next character in
sql_string to give to the lexical sql_string to give to the lexical
analyzer */ analyzer */
pars_info_t* info; /* extra information, or NULL */
sym_node_list_t sym_list; sym_node_list_t sym_list;
/* list of symbol nodes in the symbol /* list of symbol nodes in the symbol
table */ table */
...@@ -180,6 +181,7 @@ struct sym_tab_struct{ ...@@ -180,6 +181,7 @@ struct sym_tab_struct{
#define SYM_CURSOR 96 /* named cursor */ #define SYM_CURSOR 96 /* named cursor */
#define SYM_PROCEDURE_NAME 97 /* stored procedure name */ #define SYM_PROCEDURE_NAME 97 /* stored procedure name */
#define SYM_INDEX 98 /* database index name */ #define SYM_INDEX 98 /* database index name */
#define SYM_FUNCTION 99 /* user function name */
#ifndef UNIV_NONINL #ifndef UNIV_NONINL
#include "pars0sym.ic" #include "pars0sym.ic"
......
...@@ -9,6 +9,8 @@ Created 1/11/1998 Heikki Tuuri ...@@ -9,6 +9,8 @@ Created 1/11/1998 Heikki Tuuri
#ifndef pars0types_h #ifndef pars0types_h
#define 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_node_struct sym_node_t;
typedef struct sym_tab_struct sym_tab_t; typedef struct sym_tab_struct sym_tab_t;
typedef struct pars_res_word_struct pars_res_word_t; typedef struct pars_res_word_struct pars_res_word_t;
......
...@@ -78,6 +78,15 @@ fetch_step( ...@@ -78,6 +78,15 @@ fetch_step(
/*=======*/ /*=======*/
/* out: query thread to run next or NULL */ /* out: query thread to run next or NULL */
que_thr_t* thr); /* in: query thread */ 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. */ Prints a row in a select result. */
...@@ -311,6 +320,20 @@ struct fetch_node_struct{ ...@@ -311,6 +320,20 @@ struct fetch_node_struct{
que_common_t common; /* type: QUE_NODE_FETCH */ que_common_t common; /* type: QUE_NODE_FETCH */
sel_node_t* cursor_def; /* cursor definition */ sel_node_t* cursor_def; /* cursor definition */
sym_node_t* into_list; /* variables to set */ 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 */ /* Open or close cursor statement node */
......
This diff is collapsed.
This diff is collapsed.
...@@ -116,7 +116,8 @@ ...@@ -116,7 +116,8 @@
PARS_WORK_TOKEN = 342, PARS_WORK_TOKEN = 342,
PARS_UNSIGNED_TOKEN = 343, PARS_UNSIGNED_TOKEN = 343,
PARS_EXIT_TOKEN = 344, PARS_EXIT_TOKEN = 344,
NEG = 345 PARS_FUNCTION_TOKEN = 345,
NEG = 346
}; };
#endif #endif
#define PARS_INT_LIT 258 #define PARS_INT_LIT 258
...@@ -206,7 +207,8 @@ ...@@ -206,7 +207,8 @@
#define PARS_WORK_TOKEN 342 #define PARS_WORK_TOKEN 342
#define PARS_UNSIGNED_TOKEN 343 #define PARS_UNSIGNED_TOKEN 343
#define PARS_EXIT_TOKEN 344 #define PARS_EXIT_TOKEN 344
#define NEG 345 #define PARS_FUNCTION_TOKEN 345
#define NEG 346
......
...@@ -117,6 +117,7 @@ yylex(void); ...@@ -117,6 +117,7 @@ yylex(void);
%token PARS_WORK_TOKEN %token PARS_WORK_TOKEN
%token PARS_UNSIGNED_TOKEN %token PARS_UNSIGNED_TOKEN
%token PARS_EXIT_TOKEN %token PARS_EXIT_TOKEN
%token PARS_FUNCTION_TOKEN
%left PARS_AND_TOKEN PARS_OR_TOKEN %left PARS_AND_TOKEN PARS_OR_TOKEN
%left PARS_NOT_TOKEN %left PARS_NOT_TOKEN
...@@ -228,6 +229,10 @@ predefined_procedure_name: ...@@ -228,6 +229,10 @@ predefined_procedure_name:
| PARS_ASSERT_TOKEN { $$ = &pars_assert_token; } | PARS_ASSERT_TOKEN { $$ = &pars_assert_token; }
; ;
user_function_call:
PARS_ID_TOKEN '(' ')' { $$ = $1; }
;
table_list: table_list:
PARS_ID_TOKEN { $$ = que_node_list_add_last(NULL, $1); } PARS_ID_TOKEN { $$ = que_node_list_add_last(NULL, $1); }
| table_list ',' PARS_ID_TOKEN | table_list ',' PARS_ID_TOKEN
...@@ -453,7 +458,9 @@ close_cursor_statement: ...@@ -453,7 +458,9 @@ close_cursor_statement:
fetch_statement: fetch_statement:
PARS_FETCH_TOKEN PARS_ID_TOKEN PARS_INTO_TOKEN variable_list 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: column_def:
...@@ -575,10 +582,20 @@ cursor_declaration: ...@@ -575,10 +582,20 @@ cursor_declaration:
{ $$ = pars_cursor_declaration($3, $5); } { $$ = 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: declaration_list:
/* Nothing */ /* Nothing */
| cursor_declaration | declaration
| declaration_list cursor_declaration | declaration_list declaration
; ;
procedure_definition: procedure_definition:
......
...@@ -494,6 +494,10 @@ In the state 'id', only two actions are possible (defined below). */ ...@@ -494,6 +494,10 @@ In the state 'id', only two actions are possible (defined below). */
return(PARS_EXIT_TOKEN); return(PARS_EXIT_TOKEN);
} }
"FUNCTION" {
return(PARS_FUNCTION_TOKEN);
}
{ID} { {ID} {
yylval = sym_tab_add_id(pars_sym_tab_global, yylval = sym_tab_add_id(pars_sym_tab_global,
(byte*)yytext, (byte*)yytext,
......
...@@ -373,14 +373,15 @@ pars_resolve_exp_variables_and_types( ...@@ -373,14 +373,15 @@ pars_resolve_exp_variables_and_types(
} }
/* Not resolved yet: look in the symbol table for a variable /* 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); node = UT_LIST_GET_FIRST(pars_sym_tab_global->sym_list);
while (node) { while (node) {
if (node->resolved if (node->resolved
&& ((node->token_type == SYM_VAR) && ((node->token_type == SYM_VAR)
|| (node->token_type == SYM_CURSOR)) || (node->token_type == SYM_CURSOR)
|| (node->token_type == SYM_FUNCTION))
&& node->name && node->name
&& (sym_node->name_len == node->name_len) && (sym_node->name_len == node->name_len)
&& (ut_memcmp(sym_node->name, node->name, && (ut_memcmp(sym_node->name, node->name,
...@@ -786,6 +787,26 @@ pars_cursor_declaration( ...@@ -786,6 +787,26 @@ pars_cursor_declaration(
return(sym_node); 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. */ Parses a delete or update statement start. */
...@@ -1433,26 +1454,42 @@ pars_procedure_call( ...@@ -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* fetch_node_t*
pars_fetch_statement( pars_fetch_statement(
/*=================*/ /*=================*/
/* out: fetch statement node */ /* out: fetch statement node */
sym_node_t* cursor, /* in: cursor 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; sym_node_t* cursor_decl;
fetch_node_t* node; 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 = mem_heap_alloc(pars_sym_tab_global->heap, sizeof(fetch_node_t));
node->common.type = QUE_NODE_FETCH; node->common.type = QUE_NODE_FETCH;
pars_resolve_exp_variables_and_types(NULL, cursor); pars_resolve_exp_variables_and_types(NULL, cursor);
pars_resolve_exp_list_variables_and_types(NULL, into_list);
if (into_list) {
pars_resolve_exp_list_variables_and_types(NULL, into_list);
node->into_list = 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; cursor_decl = cursor->alias;
...@@ -1460,8 +1497,11 @@ pars_fetch_statement( ...@@ -1460,8 +1497,11 @@ pars_fetch_statement(
node->cursor_def = cursor_decl->cursor_def; node->cursor_def = cursor_decl->cursor_def;
if (into_list) {
ut_a(que_node_list_get_len(into_list) ut_a(que_node_list_get_len(into_list)
== que_node_list_get_len(node->cursor_def->select_list)); == que_node_list_get_len(
node->cursor_def->select_list));
}
return(node); return(node);
} }
...@@ -1822,6 +1862,7 @@ que_t* ...@@ -1822,6 +1862,7 @@ que_t*
pars_sql( pars_sql(
/*=====*/ /*=====*/
/* out, own: the query graph */ /* out, own: the query graph */
pars_info_t* info, /* in: extra information, or NULL */
const char* str) /* in: SQL string */ const char* str) /* in: SQL string */
{ {
sym_node_t* sym_node; sym_node_t* sym_node;
...@@ -1841,6 +1882,7 @@ pars_sql( ...@@ -1841,6 +1882,7 @@ pars_sql(
pars_sym_tab_global->sql_string = mem_heap_strdup(heap, str); pars_sym_tab_global->sql_string = mem_heap_strdup(heap, str);
pars_sym_tab_global->string_len = strlen(str); pars_sym_tab_global->string_len = strlen(str);
pars_sym_tab_global->next_char_pos = 0; pars_sym_tab_global->next_char_pos = 0;
pars_sym_tab_global->info = info;
yyparse(); yyparse();
...@@ -1891,3 +1933,29 @@ pars_complete_graph_for_exec( ...@@ -1891,3 +1933,29 @@ pars_complete_graph_for_exec(
return(thr); 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. */ ...@@ -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_high(new_id),
(ulong) ut_dulint_get_low(new_id)); (ulong) ut_dulint_get_low(new_id));
graph = pars_sql(buf); graph = pars_sql(NULL, buf);
ut_a(graph); ut_a(graph);
...@@ -2942,7 +2942,7 @@ do not allow the TRUNCATE. We also reserve the data dictionary latch. */ ...@@ -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_high(new_id),
(ulong) ut_dulint_get_low(new_id)); (ulong) ut_dulint_get_low(new_id));
graph = pars_sql(sql); graph = pars_sql(NULL, sql);
ut_a(graph); ut_a(graph);
...@@ -3166,7 +3166,7 @@ row_drop_table_for_mysql( ...@@ -3166,7 +3166,7 @@ row_drop_table_for_mysql(
ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX)); ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
#endif /* UNIV_SYNC_DEBUG */ #endif /* UNIV_SYNC_DEBUG */
graph = pars_sql(sql); graph = pars_sql(NULL, sql);
ut_a(graph); ut_a(graph);
mem_free(sql); mem_free(sql);
...@@ -3781,7 +3781,7 @@ row_rename_table_for_mysql( ...@@ -3781,7 +3781,7 @@ row_rename_table_for_mysql(
ut_a(sqlend == sql + len + 1); ut_a(sqlend == sql + len + 1);
graph = pars_sql(sql); graph = pars_sql(NULL, sql);
ut_a(graph); ut_a(graph);
mem_free(sql); mem_free(sql);
......
...@@ -1975,7 +1975,18 @@ fetch_step( ...@@ -1975,7 +1975,18 @@ fetch_step(
if (sel_node->state != SEL_NODE_NO_MORE_ROWS) { 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); thr->run_node = que_node_get_parent(node);
...@@ -2004,6 +2015,46 @@ fetch_step( ...@@ -2004,6 +2015,46 @@ fetch_step(
return(thr); 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. */ 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