diff options
Diffstat (limited to 'Docs')
-rw-r--r-- | Docs/INSTALL-BINARY | 24 | ||||
-rw-r--r-- | Docs/glibc-2.2.5.patch | 137 | ||||
-rw-r--r-- | Docs/linuxthreads.txt | 19 | ||||
-rw-r--r-- | Docs/sp-imp-spec.txt | 1100 |
4 files changed, 13 insertions, 1267 deletions
diff --git a/Docs/INSTALL-BINARY b/Docs/INSTALL-BINARY index 7ff33c7051e..2bd6daaea17 100644 --- a/Docs/INSTALL-BINARY +++ b/Docs/INSTALL-BINARY @@ -1,7 +1,9 @@ MariaDB and MySQL have identical install methods. In this document we -describe how to install MariaDB; However all documentation at www.mysql.com -also applies. +describe how to install MariaDB. +The full documentation for installing MariaDB can be found at +https://mariadb.com/kb/en/library/binary-packages/ +However most documentation at www.mysql.com also applies. 2.2. Installing MariaDB from Generic Binaries on Unix/Linux @@ -33,7 +35,8 @@ also applies. If you run into problems and need to file a bug report, please report them to: http://mariadb.org/jira - See the instructions in Section 1.6, "How to Report Bugs or Problems." + See the instructions at + https://mariadb.com/kb/en/mariadb-community-bug-reporting The basic commands that you must execute to install and use a MariaDB binary distribution are: @@ -79,10 +82,9 @@ shell> useradd -g mysql mysql is protected, you must perform the installation as root.) shell> cd /usr/local - 3. Obtain a distribution file using the instructions in Section - 2.1.3, "How to Get MariaDB." For a given release, binary - distributions for all platforms are built from the same MariaDB - source distribution. + 3. Obtain a distribution file using the instructions at + https://mariadb.com/kb/en/library/where-to-download-mariadb/ + The description below describes how to install a MariaDB tar file. 4. Unpack the distribution, which creates the installation directory. Then create a symbolic link to that directory: @@ -149,8 +151,8 @@ shell> chown -R mysql data machine, you can copy support-files/mysql.server to the location where your system has its startup files. More information can be found in the support-files/mysql.server - script itself and in Section 2.13.1.2, "Starting and Stopping - MariaDB Automatically." + script itself and at + https://mariadb.com/kb/en/starting-and-stopping-mariadb-automatically. 10. You can set up new accounts using the bin/mysql_setpermission script if you install the DBI and DBD::mysql Perl modules. See Section 4.6.14, "mysql_setpermission --- Interactively Set @@ -181,8 +183,8 @@ shell> bin/mysqld_safe --user=mysql & find some information in the host_name.err file in the data directory. - More information about mysqld_safe is given in Section 4.3.2, - "mysqld_safe --- MySQL Server Startup Script." + More information about mysqld_safe can be found at + https://mariadb.com/kb/en/mysqld_safe Note diff --git a/Docs/glibc-2.2.5.patch b/Docs/glibc-2.2.5.patch deleted file mode 100644 index ef5d40b6899..00000000000 --- a/Docs/glibc-2.2.5.patch +++ /dev/null @@ -1,137 +0,0 @@ -diff -r -c --exclude='*.info*' glibc-2.2.5.org/linuxthreads/internals.h glibc-2.2.5/linuxthreads/internals.h -*** glibc-2.2.5.org/linuxthreads/internals.h Thu Nov 29 08:44:16 2001 ---- glibc-2.2.5/linuxthreads/internals.h Tue May 21 10:51:53 2002 -*************** -*** 343,349 **** - THREAD_SELF implementation is used, this must be a power of two and - a multiple of PAGE_SIZE. */ - #ifndef STACK_SIZE -! #define STACK_SIZE (2 * 1024 * 1024) - #endif - - /* The initial size of the thread stack. Must be a multiple of PAGE_SIZE. */ ---- 343,349 ---- - THREAD_SELF implementation is used, this must be a power of two and - a multiple of PAGE_SIZE. */ - #ifndef STACK_SIZE -! #define STACK_SIZE (128 * 1024) - #endif - - /* The initial size of the thread stack. Must be a multiple of PAGE_SIZE. */ -diff -r -c --exclude='*.info*' glibc-2.2.5.org/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h glibc-2.2.5/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h -*** glibc-2.2.5.org/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h Thu Jun 8 21:49:49 2000 ---- glibc-2.2.5/linuxthreads/sysdeps/unix/sysv/linux/bits/local_lim.h Tue May 21 10:52:58 2002 -*************** -*** 64,70 **** - /* The number of threads per process. */ - #define _POSIX_THREAD_THREADS_MAX 64 - /* This is the value this implementation supports. */ -! #define PTHREAD_THREADS_MAX 1024 - - /* Maximum amount by which a process can descrease its asynchronous I/O - priority level. */ ---- 64,70 ---- - /* The number of threads per process. */ - #define _POSIX_THREAD_THREADS_MAX 64 - /* This is the value this implementation supports. */ -! #define PTHREAD_THREADS_MAX 4096 - - /* Maximum amount by which a process can descrease its asynchronous I/O - priority level. */ -diff -r -c --exclude='*.info*' glibc-2.2.5.org/nss/nsswitch.c glibc-2.2.5/nss/nsswitch.c -*** glibc-2.2.5.org/nss/nsswitch.c Tue Jul 17 10:21:36 2001 ---- glibc-2.2.5/nss/nsswitch.c Tue May 21 10:59:55 2002 -*************** -*** 496,501 **** ---- 496,502 ---- - { - service_user *new_service; - const char *name; -+ int name_alloc_len; - - while (isspace (line[0])) - ++line; -*************** -*** 510,522 **** - if (name == line) - return result; - - - new_service = (service_user *) malloc (sizeof (service_user) -! + (line - name + 1)); - if (new_service == NULL) - return result; - -! *((char *) __mempcpy (new_service->name, name, line - name)) = '\0'; - - /* Set default actions. */ - new_service->actions[2 + NSS_STATUS_TRYAGAIN] = NSS_ACTION_CONTINUE; ---- 511,534 ---- - if (name == line) - return result; - -+ name_alloc_len = line - name + 1; -+ -+ #ifdef DO_STATIC_NSS -+ if (!((name_alloc_len == 6 && strncmp(name,"files",5) == 0) || -+ (name_alloc_len == 4 && strncmp(name,"dns",3) == 0))) -+ { -+ name = (char*) "files"; -+ name_alloc_len = 6; -+ } -+ #endif - - new_service = (service_user *) malloc (sizeof (service_user) -! + name_alloc_len); - if (new_service == NULL) - return result; - -! *((char *) __mempcpy (new_service->name, name, name_alloc_len-1)) = '\0'; -! - - /* Set default actions. */ - new_service->actions[2 + NSS_STATUS_TRYAGAIN] = NSS_ACTION_CONTINUE; -diff -r -c --exclude='*.info*' glibc-2.2.5.org/time/Makefile glibc-2.2.5/time/Makefile -*** glibc-2.2.5.org/time/Makefile Fri Aug 10 01:59:41 2001 ---- glibc-2.2.5/time/Makefile Tue May 21 11:01:11 2002 -*************** -*** 37,44 **** - - include ../Rules - -! tz-cflags = -DTZDIR='"$(zonedir)"' \ -! -DTZDEFAULT='"$(localtime-file)"' \ - -DTZDEFRULES='"$(posixrules-file)"' - - CFLAGS-tzfile.c = $(tz-cflags) ---- 37,44 ---- - - include ../Rules - -! tz-cflags = -DTZDIR='"/usr/share/zoneinfo/"' \ -! -DTZDEFAULT='"/etc/localtime"' \ - -DTZDEFRULES='"$(posixrules-file)"' - - CFLAGS-tzfile.c = $(tz-cflags) -diff -r -c --exclude='*.info*' glibc-2.2.5.org/timezone/Makefile glibc-2.2.5/timezone/Makefile -*** glibc-2.2.5.org/timezone/Makefile Thu Aug 30 00:45:25 2001 ---- glibc-2.2.5/timezone/Makefile Tue May 21 11:01:57 2002 -*************** -*** 159,166 **** - - $(objpfx)zic: $(objpfx)scheck.o $(objpfx)ialloc.o - -! tz-cflags = -DTZDIR='"$(zonedir)"' \ -! -DTZDEFAULT='"$(localtime-file)"' \ - -DTZDEFRULES='"$(posixrules-file)"' \ - -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone - ---- 159,166 ---- - - $(objpfx)zic: $(objpfx)scheck.o $(objpfx)ialloc.o - -! tz-cflags = -DTZDIR='"/usr/share/zoneinfo/"' \ -! -DTZDEFAULT='"/etc/localtime"' \ - -DTZDEFRULES='"$(posixrules-file)"' \ - -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone - diff --git a/Docs/linuxthreads.txt b/Docs/linuxthreads.txt deleted file mode 100644 index 552415fe794..00000000000 --- a/Docs/linuxthreads.txt +++ /dev/null @@ -1,19 +0,0 @@ -[Note this information is obsolete] - -Notes on compiling glibc for the standard MySQL binary: - - - make sure you have gcc 2.95 and gmake 3.79 or newer - - wget ftp://ftp.gnu.org/pub/gnu/glibc/glibc-2.2.5.tar.gz - - wget ftp://ftp.gnu.org/pub/gnu/glibc/glibc-linuxthreads-2.2.5.tar.gz - - tar zxvf glibc-2.2.5.tar.gz ; cd glibc-2.2.5 ; - tar zxvf ../glibc-linuxthreads-2.2.5.tar.gz - - patch -p1 < ~/bk/mysql/Docs/glibc-2.2.5.patch - - ./configure --prefix=/usr/local/mysql-glibc --enable-static-nss \ - --disable-shared --enable-add-ons=linuxthreads --target=i386 \ - --host=i386-pc-linux-gnu - - make - - possible problems - if compiler is not properly installed, one can get - "cpp: too many input" files error - easiest way to solve - SUSE RPM for gcc - 2.95 - - surun make install - - To build the binaries, run Build-tools/Do-linux-build diff --git a/Docs/sp-imp-spec.txt b/Docs/sp-imp-spec.txt deleted file mode 100644 index 52389ea50f4..00000000000 --- a/Docs/sp-imp-spec.txt +++ /dev/null @@ -1,1100 +0,0 @@ - - Implementation specification for Stored Procedures - ================================================== - - -- How parsing and execution of queries work - - In order to execute a query, the function sql_parse.cc:mysql_parse() is - called, which in turn calls the parser (yyparse()) with an updated Lex - structure as the result. mysql_parse() then calls mysql_execute_command() - which dispatches on the command code (in Lex) to the corresponding code for - executing that particular query. - - There are three structures involved in the execution of a query which are of - interest to the stored procedure implementation: - - - Lex (mentioned above) is the "compiled" query, that is the output from - the parser and what is then interpreted to do the actual work. - It constains an enum value (sql_command) which is the query type, and - all the data collected by the parser needed for the execution (table - names, fields, values, etc). - - THD is the "run-time" state of a connection, containing all that is - needed for a particular client connection, and, among other things, the - Lex structure currently being executed. - - Item_*: During parsing, all data is translated into "items", objects of - the subclasses of "Item", such as Item_int, Item_real, Item_string, etc, - for basic datatypes, and also various more specialized Item types for - expressions to be evaluated (Item_func objects). - - -- How to fit Stored Procedure into this scheme - - - An overview of the classes and files for stored procedures - (More detailed APIs at the end of this file) - - - class sp_head (sp_head.{cc,h}) - This contains, among other things, an array of "instructions" and the - method for executing the procedure. - - - class sp_pcontext (sp_pcontext.{cc,h} - This is the parse context for the procedure. It's primarily used during - parsing to keep track of local parameters, variables and labels, but - it's also used at CALL time do find parameters mode (IN, OUT or INOUT) - and type when setting up the runtime context. - - - class sp_instr (sp_head.{cc,h}) - This is the base class for "instructions", that is, what is generated - by the parser. It turns out that we only need a minimum of 5 different - sub classes: - - sp_instr_stmt - Execute a statement. This is the "call-out" any normal SQL statement, - like a SELECT, INSERT etc. It contains the Lex structure for the - statement in question. - - sp_instr_set - Set the value of a local variable (or parameter) - - sp_instr_jump - An unconditional jump. - - sp_instr_jump_if_not - Jump if condition is not true. It turns out that the negative test is - most convenient when generating the code for the flow control - constructs. - - sp_instr_freturn - Return a value from a FUNCTION and exit. - For condition HANDLERs some special instructions are also needed, see - that section below. - - - class sp_rcontext (sp_rcontext.h) - This is the runtime context in the THD structure. - It contains an array of items, the parameters and local variables for - the currently executing stored procedure. - This means that variable value lookup is in runtime is constant time, - a simple index operation. - - - class Item_splocal (Item.{cc,h}) - This is a subclass of Item. Its sole purpose is to hide the fact that - the real Item is actually in the current frame (runtime context). - It contains the frame offset and defers all methods to the real Item - in the frame. This is what the parser generates for local variables. - - - Utility functions (sp.{cc,h}) - This contains functions for creating, dropping and finding a stored - procedure in the mysql.proc table (or the internal cache). - - - - Parsing CREATE PROCEDURE ... - - When parsing a CREATE PROCEDURE the parser first initializes the - sphead and spcont (runtime context) fields in the Lex. - The sql_command code for the result of parsing a is - SQLCOM_CREATE_PROCEDURE. - - The parsing of the parameter list and body is relatively - straight-forward: - - - Parameters: - name, type and mode (IN/OUT/INOUT) is pushed to spcont - - Declared local variables: - Same as parameters (mode is then IN) - - Local Variable references: - If an identifier is found in in spcont, an Item_splocal is created - with the variable's frame index, otherwise an Item_field or Item_ref - is created (as before). - - Statements: - The Lex in THD is replaced by a new Lex structure and the statement, - is parsed as usual. A sp_instr_stmt is created, containing the new - Lex, and added to the instructions in sphead. - Afterwards, the procedure's Lex is restored in THD. - - SET var: - Setting a local variable generates a sp_instr_set instruction, - containing the variable's frame offset, the expression (an Item), - and the type. - - Flow control: - Flow control constructs like, IF, WHILE, etc, generate a conditional - and unconditional jumps in the "obvious" way, but a few notes may - be required: - - Forward jumps: When jumping forward, the exact destination is not - known at the time of the creation of the jump instruction. The - sphead therefore contains list of instruction-label pairs for - each forward reference. When the position later is known, the - instructions in the list are updated with the correct location. - - Loop constructs have optional labels. If a loop doesn't have a - label, an anonymous label is generated to simplify the parsing. - - There are two types of CASE. The "simple" case is implemented - with an anonymous variable bound to the value to be tested. - - - - A simple example - - Parsing the procedure: - - create procedure a(s char(16)) - begin - declare x int; - set x = 3; - while x > 0 do - set x = x-1; - insert into db.tab values (x, s); - end while; - end - - would generate the following structures: - ______ - thd: | | _________ - | lex -+--->| | ___________________ - |______| | spcont -+------------------->| "s",in,char(16):0 | - | sphead -+------ |("x",in,int :1)| - |_________| | |___________________| - ____V__________________ - | m_name: "a" | - | m_defstr: "create ..."| - | m_instr: ... | - |_______________________| - - Note that the contents of the spcont is changing during the parsing, - at all times reflecting the state of the would-be runtime frame. - The m_instr is an array of instructions: - - Pos. Instruction - 0 sp_instr_set(1, '3') - 1 sp_instr_jump_if_not(5, 'x>0') - 2 sp_instr_set(1, 'x-1') - 3 sp_instr_stmt('insert into ...') - 4 sp_instr_jump(1) - 5 <end> - - Here, '3', 'x>0', etc, represent the Items or Lex for the respective - expressions or statements. - - - - Parsing CREATE FUNCTION ... - - Creating a functions is essentially the same thing as for a PROCEDURE, - with the addition that a FUNCTION has a return type and a RETURN - statement, but no OUT or INOUT parameters. - - The main difference during parsing is that we store the result type - in the sp_head. However, there are big differences when it comes to - invoking a FUNCTION. (See below.) - - - - Storing, caching, dropping... - - As seen above, the entired definition string, including the "CREATE - PROCEDURE" (or "FUNCTION") is kept. The procedure definition string is - stored in the table mysql.proc with the name and type as the key, the - type being one of the enum ("procedure","function"). - - A PROCEDURE is just stored in the mysql.proc table. A FUNCTION has an - additional requirement. They will be called in expressions with the same - syntax as UDFs, so UDFs and stored FUNCTIONs share the namespace. Thus, - we must make sure that we do not have UDFs and FUNCTIONs with the same - name (even if they are stored in different places). - - This means that we can reparse the procedure as many time as we want. - The first time, the resulting Lex is used to store the procedure in - the database (using the function sp.c:sp_create_procedure()). - - The simplest way would be to just leave it at that, and re-read the - procedure from the database each time it is called. (And in fact, that's - the way the earliest implementation will work.) - However, this is not very efficient, and we can do better. The full - implementation should work like this: - - 1) Upon creation time, parse and store the procedure. Note that we still - need to parse it to catch syntax errors, but we can't check if called - procedures exists for instance. - 2) Upon first CALL, read from the database, parse it, and cache the - resulting Lex in memory. This time we can do more error checking. - 3) Upon subsequent CALLs, use the cached Lex. - - Note that this implies that the Lex structure with its sphead must be - reentrant, that is, reusable and shareable between different threads - and calls. The runtime state for a procedure is kept in the sp_rcontext - in THD. - - The mechanisms of storing, finding, and dropping procedures are - encapsulated in the files sp.{cc,h}. - - - - CALLing a procedure - - A CALL is parsed just like any statement. The resulting Lex has the - sql_command SQLCOM_CALL, the procedure's name and the parameters are - pushed to the Lex' value_list. - - sql_parse.cc:mysql_execute_command() then uses sp.cc:sp_find() to - get the sp_head for the procedure (which may have been read from the - database or fetched from the in-memory cache) and calls the sp_head's - method execute(). - Note: It's important that substatements called by the procedure do not - do send_ok(). Fortunately, there is a flag in THD->net to disable - this during CALLs. If a substatement fails, it will however send - an error back to the client, so the CALL mechanism must return - immediately and without sending an error. - - The sp_head::execute() method works as follows: - - 1) Keep a pointer to the old runtime context in THD (if any) - 2) Create a new runtime context. The information about the required size - is in sp_head's parse time context. - 3) Push each parameter (from the CALL's Lex->value_list) to the new - context. If it's an OUT or INOUT parameter, the parameter's offset - in the caller's frame is set in the new context as well. - 4) For each instruction, call its execute() method. - The result is a pointer to the next instruction to execute (or NULL) - if an error occurred. - 5) On success, set the new values of the OUT and INOUT parameters in - the caller's frame. - - - USE database - - Before executing the instruction we also keeps the current default - database (if any). If this was changed during execution (i.e. a "USE" - statement has been executed), we restore the current database to the - original. - - This is the most useful way to handle USE in procedures. If we didn't, - the caller would find himself in a different database after calling - a function, which can be confusing. - Restoring the database also gives full freedom to the procedure writer: - - It's possible to write "general" procedures that are independent of - the actual database name. - - It's possible to write procedures that work on a particular database - by calling USE, without having to use fully qualified table names - everywhere (which doesn't help if you want to call other, "general", - procedures anyway). - - - Evaluating Items - - There are three occasions where we need to evaluate an expression: - - - When SETing a variable - - When CALLing a procedure - - When testing an expression for a branch (in IF, WHILE, etc) - - The semantics in stored procedures is "call-by-value", so we have to - evaluate any "func" Items at the point of the CALL or SET, otherwise - we would get a kind of "lazy" evaluation with unexpected results with - respect to OUT parameters for instance. - For this the support function, sp_head.cc:eval_func_item() is needed. - - - - Calling a FUNCTION - - Functions don't have an explicit call keyword like procedures. Instead, - they appear in expressions with the conventional syntax "fun(arg, ...)". - The problem is that we already have User Defined Functions (UDFs) which - are called the same way. A UDF is detected by the lexical analyzer (not - the parser!), in the find_keyword() function, and returns a UDF_*_FUNC - or UDA_*_SUM token with the udf_func object as the yylval. - - So, stored functions must be handled in a simpilar way, and as a - consequence, UDFs and functions must not have the same name. - - - Detecting and parsing a FUNCTION invocation - - The existence of UDFs are checked during the lexical analysis (in - sql_lex.cc:find_keyword()). This has the drawback that they must - exist before they are referred to, which was ok before SPs existed, - but then it becomes a problem. The first implementation of SP FUNCTIONs - will work the same way, but this should be fixed a.s.a.p. (This will - required some reworking of the way UDFs are handled, which is why it's - not done from the start.) - For the time being, a FUNCTION is detected the same way, and returns - the token SP_FUNC. During the parsing we only check for the *existence* - of the function, we don't parse it, since wa can't call the parser - recursively. - - When encountering a SP_FUNC with parameters in the expression parser, - an instance of the new Item_func_sp class is created. Unlike UDFs, we - don't have different classes for different return types, since we at - this point don't know the type. - - - Collecting FUNCTIONs to invoke - - A FUNCTION differs from a PROCEDURE in one important aspect: Whereas a - PROCEDURE is CALLed as statement by itself, a FUNCTION is invoked - "on-the-fly" during the execution of *another* statement. - This makes things a lot more complicated compared to CALL: - - We can't read and parse the FUNCTION from the mysql.proc table at the - point of invocation; the server requires that all tables used are - opened and locked at the beginning of the query execution. - One "obvious" solution would be to simply push "mysql.proc" to the list - of tables used by the query, but this implies a "join" with this table - if the query is a select, so it doesn't work (and we can't exclude this - table easily; since a privileged used might in fact want to search - the proc table). - Another solution would of course be to allow the opening and closing - of the mysql.proc table during a query execution, but this it not - possible at the present. - - So, the solution is to collect the names of the referred FUNCTIONs during - parsing in the lex. - Then, before doing anything else in mysql_execute_command(), read all - functions from the database an keep them in the THD, where the function - sp_find_function() can find them during the execution. - Note: Even with an in-memory cache, we must still make sure that the - functions are indeed read and cached at this point. - The code that read and cache functions from the database must also be - invoked recursively for each read FUNCTION to make sure we have *all* the - functions we need. - - - - Parsing DROP PROCEDURE/FUNCTION - - The procedure name is pushed to Lex->value_list. - The sql_command code for the result of parsing a is - SQLCOM_DROP_PROCEDURE/SQLCOM_DROP_FUNCTION. - - Dropping is done by simply getting the procedure with the sp_find() - function and calling sp_drop() (both in sp.{cc,h}). - - DROP PROCEDURE/FUNCTION also supports the non-standard "IF EXISTS", - analogous to other DROP statements in MySQL. - - - - Condition and Handlers - - Condition names are lexical entities and are kept in the parser context - just like variables. But, condition are just "aliases" for SQLSTATE - strings, or mysqld error codes (which is a non-standard extension in - MySQL), and are only used during parsing. - - Handlers comes in three types, CONTINUE, EXIT and UNDO. The latter is - like an EXIT handler with an implicit rollback, and is currently not - implemented. - The EXIT handler jumps to the end of its BEGIN-END block when finished. - The CONTINUE handler returns to the statement following that which - invoked the handler. - - The handlers in effect at any point is part of each thread's runtime - state, so we need to push and pop handlers in the sp_rcontext during - execution. We use special instructions for this: - - sp_instr_hpush_jump - Push a handler. The instruction contains the necessary information, - like which conditions we handle and the location of the handler. - The jump takes us to the location after the handler code. - - sp_instr_hpop - Pop the handlers of the current frame (which we are just leaving). - - It might seems strange to jump past the handlers like that, but there's - no extra cost in doing this, and for technical reasons it's easiest for - the parser to generate the handler instructions when they occur in the - source. - - When an error occurs, one of the error routines is called and an error - message is normally sent back to the client immediately. - Catching a condition must be done in these error routines (there are - quite a few) to prevent them from doing this. We do this by calling - a method in the THD's sp_rcontext (if there is one). If a handler is - found, this is recorded in the context and the routine returns without - sending the error message. - The execution loop (sp_head::execute()) checks for this after each - statement and invokes the handler that has been found. If several - errors or warnings occurs during one statement, only the first is - caught, the rest are ignored. - - Invoking and returning from a handler is trivial in the EXIT case. - We simply jump to it, and it will have an sp_instr_jump as its last - instruction. - - Calling and returning from a CONTINUE handler poses some special - problems. Since we need to return to the point after its invocation, - we push the return location on a stack in the sp_rcontext (this is - done by the execution loop). The handler then ends with a special - instruction, sp_instr_hreturn, which returns to this location. - - CONTINUE handlers have one additional problem: They are parsed at - the lexical level where they occur, so variable offsets will assume - that it's actually called at that level. However, a handler might be - invoked from a sub-block where additional local variables have been - declared, which will then share the location of any local variables - in the handler itself. So, when calling a CONTINUE handler, we need - to save any local variables above the handler's frame offset, and - restore them upon return. (This is not a problem for EXIT handlers, - since they will leave the block anyway.) - This is taken care of by the execution loop and the sp_instr_hreturn - instruction. - - - Examples: - - - EXIT handler - begin - declare x int default 0; - - begin - declare exit handler for 'XXXXX' set x = 1; - - (statement1); - (statement2); - end; - (statement3); - end - - Pos. Instruction - 0 sp_instr_set(0, '0') - 1 sp_instr_hpush_jump(4, 1) # location and frame size - 2 sp_instr_set(0, '1') - 3 sp_instr_jump(6) - 4 sp_instr_stmt('statement1') - 5 sp_instr_stmt('statement2') - 6 sp_instr_hpop(1) - 7 sp_instr_stmt('statement3') - - - CONTINUE handler - create procedure hndlr1(val int) - begin - declare x int default 0; - declare foo condition for 1146; - declare continue handler for foo set x = 1; - - insert into t3 values ("hndlr1", val); # Non-existing table? - if x>0 then - insert into t1 values ("hndlr1", val); # This instead then - end if; - end| - - Pos. Instruction - 0 sp_instr_set(1, '0') - 1 sp_instr_hpush_jump(4, 2) - 2 sp_instr_set(1, '1') - 3 sp_instr_hreturn(2) # frame size - 4 sp_instr_stmt('insert ... t3 ...') - 5 sp_instr_jump_if_not(7, 'x>0') - 6 sp_instr_stmt('insert ... t1 ...') - 7 sp_instr_hpop(2) - - - - Cursors - - For stored procedures to be really useful, you want to have cursors. - MySQL doesn't yet have "real" cursor support (with API and ODBC support, - allowing updating, arbitrary scrolling, etc), but a simple asensitive, - non-scrolling, read-only cursor can be implemented in SPs using the - class Protocol_cursor. - This class intecepts the creation and sending of results sets and instead - stores it in-memory, as MYSQL_FIELDS and MYSQL_ROWS (as in the client API). - - To support this, we need the usual name binding support in sp_pcontext - (similar to variables and conditions) to keep track on declared cursor - names, and a corresponding run-time mechanism in sp_rcontext. - Cursors are lexically scoped like everything with a body or BEGIN/END - block, so they are pushed and poped as usual (see conditions and variables - above). - The basic operations on a cursor are OPEN, FETCH and CLOSE, which will - each have a corresponding instruction. In addition, we need instructions - to push a new cursor (this will encapsulate the LEX of the SELECT statement - of the cursor), and a pop instruction: - - sp_instr_cpush - Push a cursor to the sp_rcontext. This instruction contains the LEX - for the select statement - - sp_instr_cpop - Pop a number of cursors from the sp_rcontext. - - sp_instr_copen - Open a cursor: This will execute the select and get the result set - in a sepeate memroot. - - sp_instr_cfetch - Fetch the next row from the in-memory result set. The instruction - contains a list of the variables (frame offsets) to set. - - sp_instr_cclose - Free the result set. - - A cursor is a separate class, sp_cursor (defined in sp_rcontex.h) which - encapsulates the basic operations used by the above instructions. - This class contains the LEX, Protocol_cursor object, and its memroot, - as well as the cursor's current state. - Compiling and executing is fairly straight-forward. sp_instr_copen is - a subclass of sp_instr_stmt and uses its mechanism to execute a - substatement. - - - Example: - - begin - declare x int; - declare c cursor for select a from t1; - - open c; - fetch c into x; - close c; - end - - Pos. Instruction - 0 sp_instr_cpush('select a from ...') - 1 sp_instr_copen(0) # The 0'th cursor - 2 sp_instr_cfetch(0) # Contains the variable list - 3 sp_instr_cclose(0) - 4 sp_instr_cpop(1) - - - - - The SP cache - - There are two ways to cache SPs: - - 1) one global cache, share by all threads/connections, - 2) one cache per thread. - - There are pros and cons with both methods: - - 1) Pros: Save memory, each SP only read from table once, - Cons: Needs locking (= serialization at access), requires thread-safe - data structures, - 2) Pros: Fast, no locking required (almost), limited thread-safe - requirement, - Cons: Uses more memory, each SP read from table once per thread. - - Unfortunately, we cannot use alternative 1 for the time being, as most - of the data structures to be cached (lex and items) are not reentrant - and thread-safe. (Things are modified at execution, we have THD pointers - stored everywhere, etc.) - This leaves us with alternative 2, one cache per thread; or actually - two, since we keep FUNCTIONs and PROCEDUREs in separate caches. - This is not that terrible; the only case when it will perform - significantly worse than a global cache is when we have an application - where new threads are connecting, calling a procedure, and disconnecting, - over and over again. - - The cache implementation itself is simple and straightforward, a hashtable - wrapped in a class and a C API (see APIs below). - - There is however one issue with multiple caches: dropping and altering - procedures. Normally, this should be a very rare event in a running - system; it's typically something you do during development and testing, - so it's not unthinkable that we would simply ignore the issue and let - any threads running with a cached version of an SP keep doing so until - its disconnected. - But assuming we want to keep the caches consistent with respect to drop - and alter, it can be done: - - 1) A global counter is needed, initialized to 0 at start. - 2) At each DROP or ALTER, increase the counter by one. - 3) Each cache has its own copy of the counter, copied at the last read. - 4) When looking up a name in the cache, first check if the global counter - is larger than the local copy. - If so, clear the cache and return "not found", and update the local - counter; otherwise, lookup as usual. - - This minimizes the cost to a single brief lock for the access of an - integer when operating normally. Only in the event of an actual drop or - alter, is the cache cleared. This may seem to be drastic, but since we - assume that this is a rare event, it's not a problem. - It would of course be possible to have a much more fine-grained solution, - keeping track of each SP, but the overhead of doing so is not worth the - effort. - - - - Class and function APIs - This is an outline of the key types. Some types and other details - in the actual files have been omitted for readability. - - - The parser context: sp_pcontext.h - - typedef enum - { - sp_param_in, - sp_param_out, - sp_param_inout - } sp_param_mode_t; - - typedef struct - { - LEX_STRING name; - enum enum_field_types type; - sp_param_mode_t mode; - uint offset; // Offset in current frame - my_bool isset; - } sp_pvar_t; - - typedef struct sp_cond_type - { - enum { number, state, warning, notfound, exception } type; - char sqlstate[6]; - uint mysqlerr; - } sp_cond_type_t; - - class sp_pcontext - { - sp_pcontext(); - - // Return the maximum frame size - uint max_framesize(); - - // Return the current frame size - uint current_framesize(); - - // Return the number of parameters - uint params(); - - // Set the number of parameters to the current frame size - void set_params(); - - // Set type of the variable at offset 'i' in the frame - void set_type(uint i, enum enum_field_types type); - - // Mark the i:th variable to "set" (i.e. having a value) with - // 'val' true. - void set_isset(uint i, my_bool val); - - // Push the variable 'name' to the frame. - void push_var(LEX_STRING *name, - enum enum_field_types type, sp_param_mode_t mode); - - // Pop 'num' variables from the frame. - void pop_var(uint num = 1); - - // Find variable by name - sp_pvar_t *find_pvar(LEX_STRING *name); - - // Find variable by index - sp_pvar_t *find_pvar(uint i); - - // Push label 'name' of instruction index 'ip' to the label context - sp_label_t *push_label(char *name, uint ip); - - // Find label 'name' in the context - sp_label_t *find_label(char *name); - - // Return the last pushed label - sp_label_t *last_label(); - - // Return and remove the last pushed label. - sp_label_t *pop_label(); - - // Push a condition to the context - void push_cond(LEX_STRING *name, sp_cond_type_t *val); - - // Pop a 'num' condition from the context - void pop_cond(uint num); - - // Find a condition in the context - sp_cond_type_t *find_cond(LEX_STRING *name); - - // Increase the handler count - void add_handler(); - - // Returns the handler count - uint handlers(); - - // Push a cursor - void push_cursor(LEX_STRING *name); - - // Find a cursor - my_bool find_cursor(LEX_STRING *name, uint *poff); - - // Pop 'num' cursors - void pop_cursor(uint num); - - // Return the number of cursors - uint cursors(); - } - - - - The run-time context (call frame): sp_rcontext.h - - #define SP_HANDLER_NONE 0 - #define SP_HANDLER_EXIT 1 - #define SP_HANDLER_CONTINUE 2 - #define SP_HANDLER_UNDO 3 - - typedef struct - { - struct sp_cond_type *cond; - uint handler; // Location of handler - int type; - uint foffset; // Frame offset for the handlers declare level - } sp_handler_t; - - class sp_rcontext - { - // 'fsize' is the max size of the context, 'hmax' the number of handlers, - // 'cmax' the number of cursors - sp_rcontext(uint fsize, uint hmax, , uint cmax); - - // Push value (parameter) 'i' to the frame - void push_item(Item *i); - - // Set slot 'idx' to value 'i' - void set_item(uint idx, Item *i); - - // Return the item in slot 'idx' - Item *get_item(uint idx); - - // Set the "out" index 'oidx' for slot 'idx. If it's an IN slot, - // use 'oidx' -1. - void set_oindex(uint idx, int oidx); - - // Return the "out" index for slot 'idx' - int get_oindex(uint idx); - - // Set the FUNCTION result - void set_result(Item *i); - - // Get the FUNCTION result - Item *get_result(); - - // Push handler at location 'h' for condition 'cond'. 'f' is the - // current variable frame size. - void push_handler(sp_cond_type_t *cond, uint h, int type, uint f); - - // Pop 'count' handlers - void pop_handlers(uint count); - - // Find a handler for this error. This sets the state for a found - // handler in the context. If called repeatedly without clearing, - // only the first call's state is kept. - int find_handler(uint sql_errno); - - // Returns 1 if a handler has been found, with '*ip' and '*fp' set - // to the handler location and frame size respectively. - int found_handler(uint *ip, uint *fp); - - // Clear the found handler state. - void clear_handler(); - - // Push a return address for a CONTINUE handler - void push_hstack(uint ip); - - // Pop the CONTINUE handler return stack - uint pop_hstack(); - - // Save variables from frame index 'fp' and up. - void save_variables(uint fp); - - // Restore saved variables from to frame index 'fp' and up. - void restore_variables(uint fp); - - // Push a cursor for the statement (lex) - void push_cursor(LEX *lex); - - // Pop 'count' cursors - void pop_cursors(uint count); - - // Pop all cursors - void pop_all_cursors(); - - // Get the 'i'th cursor - sp_cursor *get_cursor(uint i); - - } - - - - The procedure: sp_head.h - - #define TYPE_ENUM_FUNCTION 1 - #define TYPE_ENUM_PROCEDURE 2 - - class sp_head - { - int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE - - sp_head(); - - void init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid); - - // Store this procedure in the database. This is a wrapper around - // the function sp_create_procedure(). - int create(THD *); - - // Invoke a FUNCTION - int - execute_function(THD *thd, Item **args, uint argcount, Item **resp); - - // CALL a PROCEDURE - int - execute_procedure(THD *thd, List<Item> *args); - - // Add the instruction to this procedure. - void add_instr(sp_instr *); - - // Returns the number of instructions. - uint instructions(); - - // Returns the last instruction - sp_instr *last_instruction(); - - // Resets lex in 'thd' and keeps a copy of the old one. - void reset_lex(THD *); - - // Restores lex in 'thd' from our copy, but keeps some status from the - // one in 'thd', like ptr, tables, fields, etc. - void restore_lex(THD *); - - // Put the instruction on the backpatch list, associated with - // the label. - void push_backpatch(sp_instr *, struct sp_label *); - - // Update all instruction with this label in the backpatch list to - // the current position. - void backpatch(struct sp_label *); - - // Returns the SP name (with optional length in '*lenp'). - char *name(uint *lenp = 0); - - // Returns the result type for a function - Item_result result(); - - // Sets various attributes - void sp_set_info(char *creator, uint creatorlen, - longlong created, longlong modified, - bool suid, char *comment, uint commentlen); - } - - - - Instructions - - - The base class: - class sp_instr - { - // 'ip' is the index of this instruction - sp_instr(uint ip); - - // Execute this instrution. - // '*nextp' will be set to the index of the next instruction - // to execute. (For most instruction this will be the - // instruction following this one.) - // Returns 0 on success, non-zero if some error occurred. - virtual int execute(THD *, uint *nextp) - } - - - Statement instruction: - class sp_instr_stmt : public sp_instr - { - sp_instr_stmt(uint ip); - - int execute(THD *, uint *nextp); - - // Set the statement's Lex - void set_lex(LEX *); - - // Return the statement's Lex - LEX *get_lex(); - } - - - SET instruction: - class sp_instr_set : public sp_instr - { - // 'offset' is the variable's frame offset, 'val' the value, - // and 'type' the variable type. - sp_instr_set(uint ip, - uint offset, Item *val, enum enum_field_types type); - - int execute(THD *, uint *nextp); - } - - - Unconditional jump - class sp_instr_jump : public sp_instr - { - // No destination, must be set. - sp_instr_jump(uint ip); - - // 'dest' is the destination instruction index. - sp_instr_jump(uint ip, uint dest); - - int execute(THD *, uint *nextp); - - // Set the destination instruction 'dest'. - void set_destination(uint dest); - } - - - Conditional jump - class sp_instr_jump_if_not : public sp_instr_jump - { - // Jump if 'i' evaluates to false. Destination not set yet. - sp_instr_jump_if_not(uint ip, Item *i); - - // Jump to 'dest' if 'i' evaluates to false. - sp_instr_jump_if_not(uint ip, Item *i, uint dest) - - int execute(THD *, uint *nextp); - } - - - Return a function value - class sp_instr_freturn : public sp_instr - { - // Return the value 'val' - sp_instr_freturn(uint ip, Item *val, enum enum_field_types type); - - int execute(THD *thd, uint *nextp); - } - - - Push a handler and jump - class sp_instr_hpush_jump : public sp_instr_jump - { - // Push handler of type 'htype', with current frame size 'fp' - sp_instr_hpush_jump(uint ip, int htype, uint fp); - - int execute(THD *thd, uint *nextp); - - // Add condition for this handler - void add_condition(struct sp_cond_type *cond); - } - - - Pops handlers - class sp_instr_hpop : public sp_instr - { - // Pop 'count' handlers - sp_instr_hpop(uint ip, uint count); - - int execute(THD *thd, uint *nextp); - } - - - Return from a CONTINUE handler - class sp_instr_hreturn : public sp_instr - { - // Return from handler, and restore variables to 'fp'. - sp_instr_hreturn(uint ip, uint fp); - - int execute(THD *thd, uint *nextp); - } - - - Push a CURSOR - class sp_instr_cpush : public sp_instr_stmt - { - // Push a cursor for statement 'lex' - sp_instr_cpush(uint ip, LEX *lex) - - int execute(THD *thd, uint *nextp); - } - - - Pop CURSORs - class sp_instr_cpop : public sp_instr_stmt - { - // Pop 'count' cursors - sp_instr_cpop(uint ip, uint count) - - int execute(THD *thd, uint *nextp); - } - - - Open a CURSOR - class sp_instr_copen : public sp_instr_stmt - { - // Open the 'c'th cursor - sp_instr_copen(uint ip, uint c); - - int execute(THD *thd, uint *nextp); - } - - - Close a CURSOR - class sp_instr_cclose : public sp_instr - { - // Close the 'c'th cursor - sp_instr_cclose(uint ip, uint c); - - int execute(THD *thd, uint *nextp); - } - - - Fetch a row with CURSOR - class sp_instr_cfetch : public sp_instr - { - // Fetch next with the 'c'th cursor - sp_instr_cfetch(uint ip, uint c); - - int execute(THD *thd, uint *nextp); - - // Add a target variable for the fetch - void add_to_varlist(struct sp_pvar *var); - } - - - - Utility functions: sp.h - - #define SP_OK 0 - #define SP_KEY_NOT_FOUND -1 - #define SP_OPEN_TABLE_FAILED -2 - #define SP_WRITE_ROW_FAILED -3 - #define SP_DELETE_ROW_FAILED -4 - #define SP_GET_FIELD_FAILED -5 - #define SP_PARSE_ERROR -6 - - // Finds a stored procedure given its name. Returns NULL if not found. - sp_head *sp_find_procedure(THD *, LEX_STRING *name); - - // Store the procedure 'name' in the database. 'def' is the complete - // definition string ("create procedure ..."). - int sp_create_procedure(THD *, - char *name, uint namelen, - char *def, uint deflen, - char *comment, uint commentlen, bool suid); - - // Drop the procedure 'name' from the database. - int sp_drop_procedure(THD *, char *name, uint namelen); - - // Finds a stored function given its name. Returns NULL if not found. - sp_head *sp_find_function(THD *, LEX_STRING *name); - - // Store the function 'name' in the database. 'def' is the complete - // definition string ("create function ..."). - int sp_create_function(THD *, - char *name, uint namelen, - char *def, uint deflen, - char *comment, uint commentlen, bool suid); - - // Drop the function 'name' from the database. - int sp_drop_function(THD *, char *name, uint namelen); - - - - The cache: sp_cache.h - - /* Initialize the SP caching once at startup */ - void sp_cache_init(); - - /* Clear the cache *cp and set *cp to NULL */ - void sp_cache_clear(sp_cache **cp); - - /* Insert an SP to cache. If **cp points to NULL, it's set to a - new cache */ - void sp_cache_insert(sp_cache **cp, sp_head *sp); - - /* Lookup an SP in cache */ - sp_head *sp_cache_lookup(sp_cache **cp, char *name, uint namelen); - - /* Remove an SP from cache */ - void sp_cache_remove(sp_cache **cp, sp_head *sp); - - - - The mysql.proc schema: - - CREATE TABLE proc ( - db char(64) binary DEFAULT '' NOT NULL, - name char(64) DEFAULT '' NOT NULL, - type enum('FUNCTION','PROCEDURE') NOT NULL, - specific_name char(64) DEFAULT '' NOT NULL, - language enum('SQL') DEFAULT 'SQL' NOT NULL, - sql_data_access enum('CONTAINS_SQL') DEFAULT 'CONTAINS_SQL' NOT NULL, - is_deterministic enum('YES','NO') DEFAULT 'NO' NOT NULL, - security_type enum('INVOKER','DEFINER') DEFAULT 'DEFINER' NOT NULL, - param_list blob DEFAULT '' NOT NULL, - returns char(64) DEFAULT '' NOT NULL, - body blob DEFAULT '' NOT NULL, - definer char(77) binary DEFAULT '' NOT NULL, - created timestamp, - modified timestamp, - sql_mode set( - 'REAL_AS_FLOAT', - 'PIPES_AS_CONCAT', - 'ANSI_QUOTES', - 'IGNORE_SPACE', - 'IGNORE_BAD_TABLE_OPTIONS', - 'ONLY_FULL_GROUP_BY', - 'NO_UNSIGNED_SUBTRACTION', - 'NO_DIR_IN_CREATE', - 'POSTGRESQL', - 'ORACLE', - 'MSSQL', - 'DB2', - 'MAXDB', - 'NO_KEY_OPTIONS', - 'NO_TABLE_OPTIONS', - 'NO_FIELD_OPTIONS', - 'MYSQL323', - 'MYSQL40', - 'ANSI', - 'NO_AUTO_VALUE_ON_ZERO' - ) DEFAULT 0 NOT NULL, - comment char(64) binary DEFAULT '' NOT NULL, - PRIMARY KEY (db,name,type) - ) comment='Stored Procedures'; - - -- - |