2013-02-11 01:21:49 +01:00
/* Copyright (C) 2007-2013 Arjen G Lentz & Antony T Curtis for Open Query
2010-01-04 09:27:50 +01:00
Portions of this file copyright ( C ) 2000 - 2006 MySQL AB
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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA */
/* ======================================================================
Open Query Graph Computation Engine , based on a concept by Arjen Lentz
2013-02-28 01:16:29 +01:00
v3 implementation by Antony Curtis , Arjen Lentz , Andrew McDonnell
2010-01-04 09:27:50 +01:00
For more information , documentation , support , enhancement engineering ,
2013-02-28 01:16:29 +01:00
see http : //openquery.com/graph or contact graph@openquery.com
2010-01-04 09:27:50 +01:00
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
# ifdef USE_PRAGMA_IMPLEMENTATION
# pragma implementation // gcc: Class implementation
# endif
2013-09-06 15:54:33 +02:00
# define MYSQL_SERVER // to have THD
# include <mysql/plugin.h>
# include "sql_class.h"
2011-04-13 00:50:32 +02:00
# include <stdarg.h>
# include <stdio.h>
2010-01-04 09:27:50 +01:00
2011-04-25 17:22:25 +02:00
# include <mysql_version.h>
2010-01-04 09:27:50 +01:00
# include "ha_oqgraph.h"
# include "graphcore.h"
2013-09-06 15:54:33 +02:00
# include <sql_error.h>
# if MYSQL_VERSION_ID >= 100004
// Interim workaround for rename in sql_error.h from this point
# define MYSQL_ERROR Sql_condition
# endif
2011-04-25 17:22:25 +02:00
# include "table.h"
# include "field.h"
# include "key.h"
2013-02-11 01:07:55 +01:00
# include "unireg.h"
# include "sql_class.h"
2011-04-25 17:22:25 +02:00
2013-02-27 13:05:19 +01:00
# include "my_dbug.h"
2013-03-07 14:29:21 +01:00
// Uncomment this for extra debug, but expect a performance hit in large queries
2013-09-09 14:20:20 +02:00
# define VERBOSE_DEBUG
2013-03-07 14:29:21 +01:00
# ifdef VERBOSE_DEBUG
# else
# undef DBUG_PRINT
2013-05-19 14:13:43 +02:00
# define DBUG_PRINT(x ...)
2013-03-07 14:29:21 +01:00
# endif
2010-01-04 09:27:50 +01:00
# define OQGRAPH_STATS_UPDATE_THRESHOLD 10
using namespace open_query ;
2013-05-19 13:51:40 +02:00
/* For the moment, include code to deal with integer latches.
* I have wrapped it with this # ifdef to make it easier to find and remove
* in the future .
*/
2013-03-04 13:45:29 +01:00
# define RETAIN_INT_LATCH_COMPATIBILITY
2013-05-19 13:51:40 +02:00
# ifdef RETAIN_INT_LATCH_COMPATIBILITY
/* In normal operation, no new tables using an integer latch can be created,
* but they can still be used if they already exist , to allow for upgrades .
*
* However to ensure the legacy function is properly tested , we add a
* server variable " oggraph_allow_create_integer_latch " which if set to TRUE
* allows new engine tables to be created with integer latches .
*/
static my_bool g_allow_create_integer_latch = FALSE ;
static MYSQL_SYSVAR_BOOL ( allow_create_integer_latch , g_allow_create_integer_latch ,
PLUGIN_VAR_RQCMDARG , " Allow creation of integer latches "
" so the upgrade logic can be tested " , NULL , NULL , FALSE ) ;
# endif
2013-03-02 13:44:27 +01:00
// Table of varchar latch operations.
2013-03-04 13:45:29 +01:00
// In the future this needs to be refactactored to live somewhere else
2013-03-02 13:44:27 +01:00
struct oqgraph_latch_op_table { const char * key ; int latch ; } ;
static const oqgraph_latch_op_table latch_ops_table [ ] = {
2013-06-29 15:26:29 +02:00
{ " " , oqgraph : : NO_SEARCH } , // suggested by Arjen, use empty string instead of no_search
2013-03-02 13:44:27 +01:00
{ " dijkstras " , oqgraph : : DIJKSTRAS } ,
{ " breadth_first " , oqgraph : : BREADTH_FIRST } ,
{ NULL , - 1 }
} ;
2013-05-19 14:04:42 +02:00
static uint32 findLongestLatch ( ) {
int len = 0 ;
for ( const oqgraph_latch_op_table * k = latch_ops_table ; k & & k - > key ; k + + ) {
int s = strlen ( k - > key ) ;
if ( s > len ) {
len = s ;
}
}
return len ;
}
2013-03-07 14:29:21 +01:00
static const char * latchToCode ( int latch ) {
for ( const oqgraph_latch_op_table * k = latch_ops_table ; k & & k - > key ; k + + ) {
if ( k - > latch = = latch ) {
return k - > key ;
}
}
return " unknown " ;
}
2013-03-02 13:44:27 +01:00
2011-04-13 00:50:32 +02:00
struct oqgraph_table_option_struct
2010-01-04 09:27:50 +01:00
{
2013-08-10 17:01:12 +02:00
const char * table_name ;
2011-04-13 00:50:32 +02:00
2013-08-10 17:01:12 +02:00
const char * origid ; // name of the origin id column
const char * destid ; // name of the target id column
const char * weight ; // name of the weight column (optional)
2011-04-13 00:50:32 +02:00
} ;
# define ha_table_option_struct oqgraph_table_option_struct
2013-02-11 01:07:55 +01:00
static const ha_create_table_option oqgraph_table_option_list [ ] =
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
HA_TOPTION_STRING ( " data_table " , table_name ) ,
HA_TOPTION_STRING ( " origid " , origid ) ,
HA_TOPTION_STRING ( " destid " , destid ) ,
HA_TOPTION_STRING ( " weight " , weight ) ,
HA_TOPTION_END
2010-01-04 09:27:50 +01:00
} ;
static const char oqgraph_description [ ] =
2011-04-13 00:50:32 +02:00
" Open Query Graph Computation Engine "
2010-01-04 09:27:50 +01:00
" (http://openquery.com/graph) " ;
# if MYSQL_VERSION_ID < 50100
static bool oqgraph_init ( ) ;
handlerton oqgraph_hton = {
" OQGRAPH " ,
SHOW_OPTION_YES ,
oqgraph_description ,
DB_TYPE_OQGRAPH ,
oqgraph_init ,
0 , /* slot */
0 , /* savepoint size. */
NULL , /* close_connection */
NULL , /* savepoint */
NULL , /* rollback to savepoint */
NULL , /* release savepoint */
NULL , /* commit */
NULL , /* rollback */
NULL , /* prepare */
NULL , /* recover */
NULL , /* commit_by_xid */
NULL , /* rollback_by_xid */
NULL , /* create_cursor_read_view */
NULL , /* set_cursor_read_view */
NULL , /* close_cursor_read_view */
HTON_NO_FLAGS
} ;
# define STATISTIC_INCREMENT(X) \
statistic_increment ( table - > in_use - > status_var . X , & LOCK_status )
# define MOVE(X) move_field(X)
# define RECORDS records
# else
2011-04-25 17:22:25 +02:00
# define STATISTIC_INCREMENT(X) /* nothing */
2010-01-04 09:27:50 +01:00
# define MOVE(X) move_field_offset(X)
# define RECORDS stats.records
2013-02-11 01:07:55 +01:00
# endif
2010-01-04 09:27:50 +01:00
static bool oqgraph_init_done = 0 ;
static handler * oqgraph_create_handler ( handlerton * hton , TABLE_SHARE * table ,
MEM_ROOT * mem_root )
{
2013-02-27 13:05:19 +01:00
DBUG_PRINT ( " oq-debug " , ( " oqgraph_create_handler " ) ) ;
2010-01-04 09:27:50 +01:00
return new ( mem_root ) ha_oqgraph ( hton , table ) ;
}
2013-02-11 01:07:55 +01:00
# if MYSQL_VERSION_ID >= 50100
2010-01-04 09:27:50 +01:00
static int oqgraph_init ( handlerton * hton )
{
# else
static bool oqgraph_init ( )
{
if ( have_oqgraph = = SHOW_OPTION_DISABLED )
return 1 ;
2013-02-11 01:07:55 +01:00
# endif
2013-02-27 13:05:19 +01:00
DBUG_PRINT ( " oq-debug " , ( " oqgraph_init " ) ) ;
2010-01-04 09:27:50 +01:00
# if MYSQL_VERSION_ID >= 50100
hton - > state = SHOW_OPTION_YES ;
2010-01-04 14:32:42 +01:00
hton - > db_type = DB_TYPE_AUTOASSIGN ;
2010-01-04 09:27:50 +01:00
hton - > create = oqgraph_create_handler ;
2013-08-15 15:28:36 +02:00
hton - > flags = HTON_ALTER_NOT_SUPPORTED ;
// Prevent ALTER, because the core crashes when the user provides a
// non-existing backing store field for ORIGID, etc
// 'Fixes' bug 1134355
// HTON_NO_FLAGS;
2013-09-06 15:54:33 +02:00
hton - > table_options = ( ha_create_table_option * ) oqgraph_table_option_list ;
2010-01-04 09:27:50 +01:00
oqgraph_init_done = TRUE ;
return 0 ;
}
static int oqgraph_fini ( void * )
{
2013-02-27 13:05:19 +01:00
DBUG_PRINT ( " oq-debug " , ( " oqgraph_fini " ) ) ;
2010-01-04 09:27:50 +01:00
oqgraph_init_done = FALSE ;
# endif
return 0 ;
}
static int error_code ( int res )
{
switch ( res )
{
case oqgraph : : OK :
return 0 ;
case oqgraph : : NO_MORE_DATA :
return HA_ERR_END_OF_FILE ;
case oqgraph : : EDGE_NOT_FOUND :
return HA_ERR_KEY_NOT_FOUND ;
case oqgraph : : INVALID_WEIGHT :
return HA_ERR_AUTOINC_ERANGE ;
case oqgraph : : DUPLICATE_EDGE :
return HA_ERR_FOUND_DUPP_KEY ;
case oqgraph : : CANNOT_ADD_VERTEX :
case oqgraph : : CANNOT_ADD_EDGE :
return HA_ERR_RECORD_FILE_FULL ;
case oqgraph : : MISC_FAIL :
default :
return HA_ERR_CRASHED_ON_USAGE ;
}
}
/**
* Check if table complies with our designated structure
*
* ColName Type Attributes
* = = = = = = = = = = = = = = = = = = = = = = = = = = = =
2013-03-02 13:44:27 +01:00
* latch VARCHAR NULL
2010-01-04 09:27:50 +01:00
* origid BIGINT UNSIGNED NULL
* destid BIGINT UNSIGNED NULL
* weight DOUBLE NULL
* seq BIGINT UNSIGNED NULL
* linkid BIGINT UNSIGNED NULL
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
2013-03-02 13:44:27 +01:00
2013-05-19 14:13:43 +02:00
The latch may be a varchar of any length , however if it is too short to
hold the longest latch value , table creation is aborted .
2013-03-02 13:44:27 +01:00
2010-01-04 09:27:50 +01:00
CREATE TABLE foo (
2013-03-04 13:45:29 +01:00
latch VARCHAR ( 32 ) NULL ,
2010-01-04 09:27:50 +01:00
origid BIGINT UNSIGNED NULL ,
destid BIGINT UNSIGNED NULL ,
weight DOUBLE NULL ,
seq BIGINT UNSIGNED NULL ,
linkid BIGINT UNSIGNED NULL ,
KEY ( latch , origid , destid ) USING HASH ,
KEY ( latch , destid , origid ) USING HASH
) ENGINE = OQGRAPH
2013-02-28 06:49:53 +01:00
DATA_TABLE = bar
2011-04-13 00:50:32 +02:00
ORIGID = src_id
DESTID = tgt_id
2010-01-04 09:27:50 +01:00
2013-05-19 13:51:40 +02:00
Previously latch could be an integer .
We no longer allow new integer tables to be created , but we need to support
them if in use and this module is upgraded .
So when the table is opened we need to see whether latch is a varchar or
integer and change behaviour accordingly .
Note that if a table was constructed with varchar and an attempt is made to
select with latch = ( some integer number ) then MYSQL will autocast
and no data will be returned . . . so retaining compatibility does not and cannot
extend to making old queries work with new style tables .
This method is only called on table creation , so here we ensure new tables
can only be created with varchar .
This does present a small problem with regression testing ;
so we work around that by using an system variable to allow
integer latch tables to be created .
2010-01-04 09:27:50 +01:00
*/
2013-03-04 13:45:29 +01:00
int ha_oqgraph : : oqgraph_check_table_structure ( TABLE * table_arg )
2010-01-04 09:27:50 +01:00
{
2013-03-04 13:45:29 +01:00
// Changed from static so we can do decent error reporting.
2010-01-04 09:27:50 +01:00
int i ;
struct { const char * colname ; int coltype ; } skel [ ] = {
2013-03-04 13:45:29 +01:00
{ " latch " , MYSQL_TYPE_VARCHAR } ,
2010-01-04 09:27:50 +01:00
{ " origid " , MYSQL_TYPE_LONGLONG } ,
{ " destid " , MYSQL_TYPE_LONGLONG } ,
{ " weight " , MYSQL_TYPE_DOUBLE } ,
{ " seq " , MYSQL_TYPE_LONGLONG } ,
{ " linkid " , MYSQL_TYPE_LONGLONG } ,
{ NULL , 0 }
} ;
2013-03-04 13:45:29 +01:00
DBUG_ENTER ( " oqgraph_check_table_structure " ) ;
DBUG_PRINT ( " oq-debug " , ( " Checking structure. " ) ) ;
2010-01-04 09:27:50 +01:00
Field * * field = table_arg - > field ;
for ( i = 0 ; * field & & skel [ i ] . colname ; i + + , field + + ) {
2013-03-04 13:45:29 +01:00
DBUG_PRINT ( " oq-debug " , ( " Column %d: name='%s', expected '%s'; type=%d, expected %d. " , i , ( * field ) - > field_name , skel [ i ] . colname , ( * field ) - > type ( ) , skel [ i ] . coltype ) ) ;
bool badColumn = false ;
bool isLatchColumn = strcmp ( skel [ i ] . colname , " latch " ) = = 0 ;
bool isStringLatch = true ;
2013-05-19 13:51:40 +02:00
2013-03-04 13:45:29 +01:00
# ifdef RETAIN_INT_LATCH_COMPATIBILITY
2013-05-19 13:51:40 +02:00
if ( g_allow_create_integer_latch & & isLatchColumn & & ( ( * field ) - > type ( ) = = MYSQL_TYPE_SHORT ) )
2013-03-04 13:45:29 +01:00
{
2013-05-19 14:35:07 +02:00
DBUG_PRINT ( " oq-debug " , ( " Allowing integer latch anyway! " ) ) ;
2013-03-04 13:45:29 +01:00
isStringLatch = false ;
/* Make a warning */
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN ,
ER_WARN_DEPRECATED_SYNTAX , ER ( ER_WARN_DEPRECATED_SYNTAX ) ,
" latch SMALLINT UNSIGNED NULL " , " 'latch VARCHAR(32) NULL' " ) ;
} else
2013-05-19 13:51:40 +02:00
# endif
2013-05-19 14:35:07 +02:00
if ( isLatchColumn & & ( ( * field ) - > type ( ) = = MYSQL_TYPE_SHORT ) )
{
DBUG_PRINT ( " oq-debug " , ( " Allowing integer no more! " ) ) ;
badColumn = true ;
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Integer latch is not supported for new tables. " , i ) ;
} else
2010-01-04 09:27:50 +01:00
/* Check Column Type */
2013-03-04 13:45:29 +01:00
if ( ( * field ) - > type ( ) ! = skel [ i ] . coltype ) {
badColumn = true ;
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Column %d is wrong type. " , i ) ;
}
2013-05-19 14:04:42 +02:00
// Make sure latch column is large enough for all possible latch values
2013-05-19 14:35:07 +02:00
if ( isLatchColumn & & isStringLatch ) {
2013-05-19 14:04:42 +02:00
if ( ( * field ) - > char_length ( ) < findLongestLatch ( ) ) {
badColumn = true ;
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Column %d is too short. " , i ) ;
}
}
2013-03-04 13:45:29 +01:00
if ( ! badColumn ) if ( skel [ i ] . coltype ! = MYSQL_TYPE_DOUBLE & & ( ! isLatchColumn | | ! isStringLatch ) ) {
2010-01-04 09:27:50 +01:00
/* Check Is UNSIGNED */
2013-03-04 13:45:29 +01:00
if ( ( ! ( ( * field ) - > flags & UNSIGNED_FLAG ) ) ) {
badColumn = true ;
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Column %d must be UNSIGNED. " , i ) ;
}
2010-01-04 09:27:50 +01:00
}
/* Check THAT NOT NULL isn't set */
2013-03-04 13:45:29 +01:00
if ( ! badColumn ) if ( ( * field ) - > flags & NOT_NULL_FLAG ) {
badColumn = true ;
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Column %d must be NULL. " , i ) ;
}
2010-01-04 09:27:50 +01:00
/* Check the column name */
2013-03-04 13:45:29 +01:00
if ( ! badColumn ) if ( strcmp ( skel [ i ] . colname , ( * field ) - > field_name ) ) {
badColumn = true ;
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Column %d must be named '%s'. " , i , skel [ i ] . colname ) ;
}
if ( badColumn ) {
2010-01-04 09:27:50 +01:00
DBUG_RETURN ( - 1 ) ;
2013-03-04 13:45:29 +01:00
}
2010-01-04 09:27:50 +01:00
}
2013-03-04 13:45:29 +01:00
if ( skel [ i ] . colname ) {
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Not enough columns. " ) ;
2010-01-04 09:27:50 +01:00
DBUG_RETURN ( - 1 ) ;
2013-03-04 13:45:29 +01:00
}
if ( * field ) {
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Too many columns. " ) ;
DBUG_RETURN ( - 1 ) ;
}
if ( ! table_arg - > key_info | | ! table_arg - > s - > keys ) {
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " No vaild key specification. " ) ;
DBUG_RETURN ( - 1 ) ;
}
DBUG_PRINT ( " oq-debug " , ( " Checking keys. " ) ) ;
2010-01-04 09:27:50 +01:00
KEY * key = table_arg - > key_info ;
for ( uint i = 0 ; i < table_arg - > s - > keys ; + + i , + + key )
{
Field * * field = table_arg - > field ;
/* check that the first key part is the latch and it is a hash key */
if ( ! ( field [ 0 ] = = key - > key_part [ 0 ] . field & &
2013-03-04 13:45:29 +01:00
HA_KEY_ALG_HASH = = key - > algorithm ) ) {
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Incorrect keys algorithm on key %d. " , i ) ;
2010-01-04 09:27:50 +01:00
DBUG_RETURN ( - 1 ) ;
2013-03-04 13:45:29 +01:00
}
2013-09-07 11:21:05 +02:00
if ( key - > user_defined_key_parts = = 3 )
2010-01-04 09:27:50 +01:00
{
/* KEY (latch, origid, destid) USING HASH */
/* KEY (latch, destid, origid) USING HASH */
if ( ! ( field [ 1 ] = = key - > key_part [ 1 ] . field & &
field [ 2 ] = = key - > key_part [ 2 ] . field ) & &
! ( field [ 1 ] = = key - > key_part [ 2 ] . field & &
field [ 2 ] = = key - > key_part [ 1 ] . field ) )
2013-03-04 13:45:29 +01:00
{
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Keys parts mismatch on key %d. " , i ) ;
2010-01-04 09:27:50 +01:00
DBUG_RETURN ( - 1 ) ;
2013-03-04 13:45:29 +01:00
}
2010-01-04 09:27:50 +01:00
}
2013-03-04 13:45:29 +01:00
else {
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , HA_WRONG_CREATE_OPTION , " Too many key parts on key %d. " , i ) ;
2010-01-04 09:27:50 +01:00
DBUG_RETURN ( - 1 ) ;
2013-03-04 13:45:29 +01:00
}
2010-01-04 09:27:50 +01:00
}
DBUG_RETURN ( 0 ) ;
}
/*****************************************************************************
* * OQGRAPH tables
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
ha_oqgraph : : ha_oqgraph ( handlerton * hton , TABLE_SHARE * table_arg )
2011-04-13 00:50:32 +02:00
: handler ( hton , table_arg )
2013-09-09 14:20:20 +02:00
, have_table_share ( false )
, origid ( NULL )
, destid ( NULL )
, weight ( NULL )
2011-04-13 00:50:32 +02:00
, graph_share ( 0 )
, graph ( 0 )
, error_message ( " " , 0 , & my_charset_latin1 )
2010-01-04 09:27:50 +01:00
{ }
2011-04-13 00:50:32 +02:00
ha_oqgraph : : ~ ha_oqgraph ( )
{ }
2010-01-04 09:27:50 +01:00
static const char * ha_oqgraph_exts [ ] =
{
NullS
} ;
const char * * ha_oqgraph : : bas_ext ( ) const
{
return ha_oqgraph_exts ;
}
ulonglong ha_oqgraph : : table_flags ( ) const
{
return ( HA_NO_BLOBS | HA_NULL_IN_KEY |
2010-04-13 01:23:51 +02:00
HA_REC_NOT_IN_SEQ | HA_CAN_INSERT_DELAYED |
HA_BINLOG_STMT_CAPABLE | HA_BINLOG_ROW_CAPABLE ) ;
2010-01-04 09:27:50 +01:00
}
ulong ha_oqgraph : : index_flags ( uint inx , uint part , bool all_parts ) const
{
return HA_ONLY_WHOLE_INDEX | HA_KEY_SCAN_NOT_ROR ;
}
2011-04-13 00:50:32 +02:00
bool ha_oqgraph : : get_error_message ( int error , String * buf )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
if ( error < 0 )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
buf - > append ( error_message ) ;
buf - > c_ptr_safe ( ) ;
error_message . length ( 0 ) ;
2010-01-04 09:27:50 +01:00
}
2011-04-13 00:50:32 +02:00
return false ;
2010-01-04 09:27:50 +01:00
}
2013-02-11 01:07:55 +01:00
void ha_oqgraph : : fprint_error ( const char * fmt , . . . )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
va_list ap ;
va_start ( ap , fmt ) ;
error_message . reserve ( 256 ) ;
size_t len = error_message . length ( ) ;
len + = vsnprintf ( & error_message [ len ] , 255 , fmt , ap ) ;
error_message . length ( len ) ;
va_end ( ap ) ;
2010-01-04 09:27:50 +01:00
}
2013-09-09 14:20:20 +02:00
/**
* Open the OQGRAPH engine ' table ' .
*
* An OQGRAPH table is effectively similar to a view over the underlying backing table ,
* attribute ' data_table ' , but where the result returned by a query depends on the
* value of the ' latch ' column specified to the query . Therefore ,
* when mysqld opens us , we need to open the corresponding backing table ' data_table '
*
*/
2011-04-13 00:50:32 +02:00
int ha_oqgraph : : open ( const char * name , int mode , uint test_if_locked )
2010-01-04 09:27:50 +01:00
{
2013-09-09 14:20:20 +02:00
DBUG_ENTER ( " ha_oqgraph::open " ) ;
DBUG_PRINT ( " oq-debug " , ( " open(name=%s,mode=%d,test_if_locked=%u) " , name , mode , test_if_locked ) ) ;
assert ( ! have_table_share ) ;
assert ( graph = = NULL ) ;
2013-02-27 13:05:19 +01:00
2011-04-13 00:50:32 +02:00
THD * thd = current_thd ;
oqgraph_table_option_struct * options =
reinterpret_cast < oqgraph_table_option_struct * > ( table - > s - > option_struct ) ;
2010-01-04 09:27:50 +01:00
2013-02-27 13:05:19 +01:00
// Catch cases where table was not constructed properly
2013-09-10 15:15:18 +02:00
// Note - need to return -1 so our error text gets reported
2013-02-27 13:05:19 +01:00
if ( ! options ) {
2013-02-28 06:49:53 +01:00
fprint_error ( " Invalid OQGRAPH backing store (null attributes) " ) ;
2013-09-10 15:15:18 +02:00
DBUG_RETURN ( - 1 ) ;
2013-02-27 13:05:19 +01:00
}
2013-02-28 06:49:53 +01:00
if ( ! options - > table_name | | ! * options - > table_name ) {
fprint_error ( " Invalid OQGRAPH backing store (unspecified or empty data_table attribute) " ) ;
2013-02-27 13:05:19 +01:00
// if table_name if present but doesnt actually exist, we will fail out below
// when we call open_table_def(). same probably applies for the id fields
2013-09-10 15:15:18 +02:00
DBUG_RETURN ( - 1 ) ;
2013-02-27 13:05:19 +01:00
}
2013-02-28 06:49:53 +01:00
if ( ! options - > origid | | ! * options - > origid ) {
fprint_error ( " Invalid OQGRAPH backing store (unspecified or empty origid attribute) " ) ;
2013-09-10 15:15:18 +02:00
DBUG_RETURN ( - 1 ) ;
2013-02-27 13:05:19 +01:00
}
2013-02-28 13:24:46 +01:00
if ( ! options - > destid | | ! * options - > destid ) {
2013-02-28 06:49:53 +01:00
fprint_error ( " Invalid OQGRAPH backing store (unspecified or empty destid attribute) " ) ;
2013-09-10 15:15:18 +02:00
DBUG_RETURN ( - 1 ) ;
2013-02-27 13:05:19 +01:00
}
// weight is optional
2011-04-13 00:50:32 +02:00
error_message . length ( 0 ) ;
2010-01-04 09:27:50 +01:00
2013-09-09 14:20:20 +02:00
origid = destid = weight = 0 ;
init_tmp_table_share ( thd , share , table - > s - > db . str , table - > s - > db . length , options - > table_name , " " ) ;
// What I think this code is doing:
// * Our OQGRAPH table is `database_blah/name`
// * We point p --> /name (or if table happened to be simply `name`, to `name`, dont know if this is possible)
// * plen seems to be then set to length of `database_blah/options_data_table_name`
// * then we set share->normalized_path.str and share->path.str to `database_blah/options_data_table_name`
// * I assume that this verbiage is needed so the memory used by share->path.str is set in the share mem root
// * because otherwise one could simply build the string more simply using malloc and pass it instead of "" above
2011-04-13 00:50:32 +02:00
const char * p = strend ( name ) - 1 ;
while ( p > name & & * p ! = ' \\ ' & & * p ! = ' / ' )
- - p ;
size_t tlen = strlen ( options - > table_name ) ;
2013-02-27 13:41:57 +01:00
size_t plen = ( int ) ( p - name ) + tlen + 1 ;
2010-01-04 09:27:50 +01:00
2013-09-09 14:20:20 +02:00
share - > path . str = ( char * ) alloc_root ( & share - > mem_root , plen ) ;
2011-04-13 00:50:32 +02:00
strmov ( strnmov ( share - > path . str , name , ( int ) ( p - name ) + 1 ) , options - > table_name ) ;
2010-01-04 09:27:50 +01:00
2011-04-13 00:50:32 +02:00
share - > normalized_path . str = share - > path . str ;
share - > path . length = share - > normalized_path . length = plen ;
2010-01-04 09:27:50 +01:00
2013-02-27 13:41:57 +01:00
DBUG_PRINT ( " oq-debug " , ( " share:(normalized_path=%s,path.length=%zu) " ,
share - > normalized_path . str , share - > path . length ) ) ;
2013-09-09 14:20:20 +02:00
int open_def_flags = 0 ;
# if MYSQL_VERSION_ID >= 100002
open_def_flags = GTS_TABLE ;
# endif
// We want to open the definition for the given backing table
// Once can assume this loop exists because sometimes open_table_def() fails for a reason other than not exist
// and not 'exist' is valid, because we use ha_create_table_from_engine() to force it to 'exist'
// But, ha_create_table_from_engine() is removed in MariaDB 10.0.4 (?)
// Looking inside most recent ha_create_table_from_engine(), it also calls open_table_def() so maybe this whole thing is redundant...
// Or perhaps it is needed if the backing store is a temporary table or maybe if has no records as yet...?
// Lets try without this, and see if all the tests pass...
while ( open_table_def ( thd , share , open_def_flags ) )
2011-04-13 00:50:32 +02:00
{
2013-09-10 15:15:18 +02:00
# if MYSQL_VERSION_ID < 100002
2013-09-07 11:21:05 +02:00
if ( thd - > is_error ( ) & & thd - > get_stmt_da ( ) - > sql_errno ( ) ! = ER_NO_SUCH_TABLE )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( thd - > get_stmt_da ( ) - > sql_errno ( ) ) ;
2010-01-04 09:27:50 +01:00
}
2011-04-14 04:06:43 +02:00
2011-04-13 00:50:32 +02:00
if ( ha_create_table_from_engine ( thd , table - > s - > db . str , options - > table_name ) )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( thd - > get_stmt_da ( ) - > sql_errno ( ) ) ;
2010-01-04 09:27:50 +01:00
}
2013-02-11 01:07:55 +01:00
/*mysql_reset_errors(thd, 1);*/
2011-04-13 00:50:32 +02:00
thd - > clear_error ( ) ;
continue ;
2013-09-09 14:20:20 +02:00
# else
2013-09-10 15:15:18 +02:00
open_table_error ( share , OPEN_FRM_OPEN_ERROR , ENOENT ) ;
2013-09-09 14:20:20 +02:00
free_table_share ( share ) ;
2013-09-10 15:15:18 +02:00
if ( thd - > is_error ( ) )
DBUG_RETURN ( thd - > get_stmt_da ( ) - > sql_errno ( ) ) ;
DBUG_RETURN ( HA_ERR_NO_SUCH_TABLE ) ;
2013-09-09 14:20:20 +02:00
# endif
2010-01-04 09:27:50 +01:00
}
2013-09-09 14:20:20 +02:00
2011-04-13 00:50:32 +02:00
if ( int err = share - > error )
2010-01-04 09:27:50 +01:00
{
2013-09-07 11:21:05 +02:00
open_table_error ( share , share - > error , share - > open_errno ) ;
2011-04-13 00:50:32 +02:00
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( err ) ;
2010-01-04 09:27:50 +01:00
}
2011-04-13 00:50:32 +02:00
if ( share - > is_view )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
fprint_error ( " VIEWs are not supported for an OQGRAPH backing store. " ) ;
DBUG_RETURN ( - 1 ) ;
2010-01-04 09:27:50 +01:00
}
2013-09-07 11:21:05 +02:00
if ( enum open_frm_error err = open_table_from_share ( thd , share , " " ,
2011-04-13 00:50:32 +02:00
( uint ) ( HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
HA_GET_INDEX | HA_TRY_READ_ONLY ) ,
READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD ,
thd - > open_options , edges , FALSE ) )
2010-01-04 09:27:50 +01:00
{
2013-09-10 15:15:18 +02:00
open_table_error ( share , err , EMFILE ) ; // NOTE - EMFILE is probably bogus, it reports as too many open files (!)
free_table_share ( share ) ;
DBUG_RETURN ( - 1 ) ;
}
if ( ! edges - > file )
{
fprint_error ( " Some error occurred opening table '%s' " , options - > table_name ) ;
2011-04-13 00:50:32 +02:00
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2010-01-04 09:27:50 +01:00
}
2011-04-13 00:50:32 +02:00
edges - > reginfo . lock_type = TL_READ ;
2010-01-04 09:27:50 +01:00
2011-04-13 00:50:32 +02:00
edges - > tablenr = thd - > current_tablenr + + ;
edges - > status = STATUS_NO_RECORD ;
edges - > file - > ha_start_of_new_statement ( ) ;
edges - > file - > ft_handler = 0 ;
edges - > pos_in_table_list = 0 ;
edges - > clear_column_bitmaps ( ) ;
bfill ( table - > record [ 0 ] , table - > s - > null_bytes , 255 ) ;
2011-04-14 04:06:43 +02:00
bfill ( table - > record [ 1 ] , table - > s - > null_bytes , 255 ) ;
2010-01-04 09:27:50 +01:00
2011-04-13 00:50:32 +02:00
// We expect fields origid, destid and optionally weight
origid = destid = weight = 0 ;
2011-04-14 04:06:43 +02:00
2011-04-13 00:50:32 +02:00
for ( Field * * field = edges - > field ; * field ; + + field )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
if ( strcmp ( options - > origid , ( * field ) - > field_name ) )
continue ;
if ( ( * field ) - > cmp_type ( ) ! = INT_RESULT | |
! ( ( * field ) - > flags & NOT_NULL_FLAG ) )
2010-01-04 09:27:50 +01:00
{
2013-02-11 01:07:55 +01:00
fprint_error ( " Column '%s.%s' is not a not-null integer type " ,
2011-04-13 00:50:32 +02:00
options - > table_name , options - > origid ) ;
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2010-01-04 09:27:50 +01:00
}
2011-04-13 00:50:32 +02:00
origid = * field ;
break ;
2010-01-04 09:27:50 +01:00
}
2013-02-28 13:31:10 +01:00
if ( ! origid ) {
2013-09-09 14:20:20 +02:00
fprint_error ( " Invalid OQGRAPH backing store ('%s.origid' attribute not set to a valid column of '%s') " , p + 1 , options - > table_name ) ;
2013-02-28 13:31:10 +01:00
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2013-02-28 13:31:10 +01:00
}
2011-04-13 00:50:32 +02:00
for ( Field * * field = edges - > field ; * field ; + + field )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
if ( strcmp ( options - > destid , ( * field ) - > field_name ) )
continue ;
if ( ( * field ) - > type ( ) ! = origid - > type ( ) | |
! ( ( * field ) - > flags & NOT_NULL_FLAG ) )
{
2013-02-11 01:07:55 +01:00
fprint_error ( " Column '%s.%s' is not a not-null integer type " ,
2011-04-13 00:50:32 +02:00
options - > table_name , options - > destid ) ;
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2011-04-13 00:50:32 +02:00
}
destid = * field ;
break ;
2010-01-04 09:27:50 +01:00
}
2013-02-28 13:31:10 +01:00
if ( ! destid ) {
2013-09-09 14:20:20 +02:00
fprint_error ( " Invalid OQGRAPH backing store ('%s.destid' attribute not set to a valid column of '%s') " , p + 1 , options - > table_name ) ;
2013-02-28 13:31:10 +01:00
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2013-02-28 13:31:10 +01:00
}
2013-06-29 13:56:18 +02:00
// Make sure origid column != destid column
if ( strcmp ( origid - > field_name , destid - > field_name ) = = 0 ) {
2013-09-09 14:20:20 +02:00
fprint_error ( " Invalid OQGRAPH backing store ('%s.destid' attribute set to same column as origid attribute) " , p + 1 , options - > table_name ) ;
2013-06-29 13:56:18 +02:00
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2013-06-29 13:56:18 +02:00
}
2013-02-28 13:31:10 +01:00
2011-04-13 00:50:32 +02:00
for ( Field * * field = edges - > field ; options - > weight & & * field ; + + field )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
if ( strcmp ( options - > weight , ( * field ) - > field_name ) )
continue ;
if ( ( * field ) - > result_type ( ) ! = REAL_RESULT | |
! ( ( * field ) - > flags & NOT_NULL_FLAG ) )
{
2013-02-11 01:07:55 +01:00
fprint_error ( " Column '%s.%s' is not a not-null real type " ,
2011-04-13 00:50:32 +02:00
options - > table_name , options - > weight ) ;
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2011-04-13 00:50:32 +02:00
}
weight = * field ;
break ;
2010-01-04 09:27:50 +01:00
}
2013-02-28 13:31:10 +01:00
if ( ! weight & & options - > weight ) {
2013-09-10 15:15:18 +02:00
fprint_error ( " Invalid OQGRAPH backing store ('%s.weight' attribute not set to a valid column of '%s') " , p + 1 , options - > table_name ) ;
2011-04-13 00:50:32 +02:00
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2010-01-04 09:27:50 +01:00
}
2011-04-14 04:06:43 +02:00
2011-04-13 00:50:32 +02:00
if ( ! ( graph_share = oqgraph : : create ( edges , origid , destid , weight ) ) )
{
2013-02-11 01:07:55 +01:00
fprint_error ( " Unable to create graph instance. " ) ;
2011-04-13 00:50:32 +02:00
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( - 1 ) ;
2011-04-13 00:50:32 +02:00
}
ref_length = oqgraph : : sizeof_ref ;
2011-04-14 04:06:43 +02:00
2011-04-13 00:50:32 +02:00
graph = oqgraph : : create ( graph_share ) ;
2013-09-09 14:20:20 +02:00
have_table_share = true ;
2011-04-14 04:06:43 +02:00
2013-09-09 14:20:20 +02:00
DBUG_RETURN ( 0 ) ;
2010-01-04 09:27:50 +01:00
}
2011-04-13 00:50:32 +02:00
int ha_oqgraph : : close ( void )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
oqgraph : : free ( graph ) ; graph = 0 ;
oqgraph : : free ( graph_share ) ; graph_share = 0 ;
2010-01-04 09:27:50 +01:00
2013-09-09 14:20:20 +02:00
if ( have_table_share )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
if ( edges - > file )
closefrm ( edges , 0 ) ;
free_table_share ( share ) ;
2013-09-09 14:20:20 +02:00
have_table_share = false ;
2010-01-04 09:27:50 +01:00
}
2011-04-13 00:50:32 +02:00
return 0 ;
}
2010-01-04 09:27:50 +01:00
2011-04-13 00:50:32 +02:00
void ha_oqgraph : : update_key_stats ( )
{
for ( uint i = 0 ; i < table - > s - > keys ; i + + )
{
KEY * key = table - > key_info + i ;
if ( ! key - > rec_per_key )
continue ;
if ( key - > algorithm ! = HA_KEY_ALG_BTREE )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
if ( key - > flags & HA_NOSAME )
2013-09-07 11:21:05 +02:00
key - > rec_per_key [ key - > user_defined_key_parts - 1 ] = 1 ;
2011-04-13 00:50:32 +02:00
else
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
//unsigned vertices= graph->vertices_count();
//unsigned edges= graph->edges_count();
//uint no_records= vertices ? 2 * (edges + vertices) / vertices : 2;
//if (no_records < 2)
uint
no_records = 2 ;
2013-09-07 11:21:05 +02:00
key - > rec_per_key [ key - > user_defined_key_parts - 1 ] = no_records ;
2010-01-04 09:27:50 +01:00
}
}
}
2011-04-13 00:50:32 +02:00
/* At the end of update_key_stats() we can proudly claim they are OK. */
//skey_stat_version= share->key_stat_version;
}
2010-01-04 09:27:50 +01:00
2011-04-13 00:50:32 +02:00
int ha_oqgraph : : write_row ( byte * buf )
{
2013-02-11 01:07:55 +01:00
return HA_ERR_TABLE_READONLY ;
2011-04-13 00:50:32 +02:00
}
int ha_oqgraph : : update_row ( const byte * old , byte * buf )
{
2013-02-11 01:07:55 +01:00
return HA_ERR_TABLE_READONLY ;
2011-04-13 00:50:32 +02:00
}
int ha_oqgraph : : delete_row ( const byte * buf )
{
2013-02-11 01:07:55 +01:00
return HA_ERR_TABLE_READONLY ;
2010-01-04 09:27:50 +01:00
}
int ha_oqgraph : : index_read ( byte * buf , const byte * key , uint key_len ,
enum ha_rkey_function find_flag )
{
DBUG_ASSERT ( inited = = INDEX ) ;
2013-09-10 15:14:45 +02:00
// reset before we have a cursor, so the memory is not junk, avoiding the sefgault in position() when select with order by (bug #1133093)
graph - > init_row_ref ( ref ) ;
2010-01-04 09:27:50 +01:00
return index_read_idx ( buf , active_index , key , key_len , find_flag ) ;
}
int ha_oqgraph : : index_next_same ( byte * buf , const byte * key , uint key_len )
{
int res ;
open_query : : row row ;
DBUG_ASSERT ( inited = = INDEX ) ;
if ( ! ( res = graph - > fetch_row ( row ) ) )
res = fill_record ( buf , row ) ;
table - > status = res ? STATUS_NOT_FOUND : 0 ;
return error_code ( res ) ;
}
2013-03-08 13:56:26 +01:00
# define LATCH_WAS CODE 0
# define LATCH_WAS_NUMBER 1
2013-03-02 13:44:27 +01:00
/**
* This function parse the VARCHAR ( n ) latch specification into an integer operation specification compatible with
* v1 - v3 oqgraph : : search ( ) .
*
* If the string contains a number , this is directly converted from a decimal integer .
*
* Otherwise , a lookup table is used to convert from a string constant .
*
* It is anticipated that this function ( and this file and class oqgraph ) will be refactored to do this in a nicer way .
*
* FIXME : For the time being , only handles latin1 character set .
* @ return false if parsing fails .
*/
2013-03-08 13:56:26 +01:00
static int parse_latch_string_to_legacy_int ( const String & value , int & latch )
2013-03-02 13:44:27 +01:00
{
2013-03-08 13:56:26 +01:00
// Attempt to parse as exactly an integer first.
// Note: we are strict about not having whitespace, or garbage characters,
// so that the query result gets returned properly:
// Because of the way the result is built and used in fill_result,
// we have to exactly return in the latch column what was in the latch= clause
// otherwise the rows get filtered out by the query optimiser.
// For the same reason, we cant simply treat latch='' as NO_SEARCH either.
String latchValue = value ;
2013-03-02 13:44:27 +01:00
char * eptr ;
2013-03-08 13:56:26 +01:00
unsigned long int v = strtoul ( latchValue . c_ptr_safe ( ) , & eptr , 10 ) ;
if ( ! * eptr ) {
2013-06-29 15:26:29 +02:00
// we had an unsigned number; remember 0 is valid too ('vertices' aka 'no_search'))
2013-05-31 11:59:23 +02:00
if ( v > = 0 & & v < oqgraph : : NUM_SEARCH_OP ) {
2013-03-02 13:44:27 +01:00
latch = v ;
return true ;
}
// fall through and test as a string (although it is unlikely we might have an operator starting with a number)
}
const oqgraph_latch_op_table * entry = latch_ops_table ;
for ( ; entry - > key ; entry + + ) {
2013-03-08 13:56:26 +01:00
if ( 0 = = strncmp ( entry - > key , latchValue . c_ptr_safe ( ) , latchValue . length ( ) ) ) {
2013-03-02 13:44:27 +01:00
latch = entry - > latch ;
return true ;
}
}
return false ;
}
2010-01-04 09:27:50 +01:00
int ha_oqgraph : : index_read_idx ( byte * buf , uint index , const byte * key ,
uint key_len , enum ha_rkey_function find_flag )
{
Field * * field = table - > field ;
KEY * key_info = table - > key_info + index ;
int res ;
VertexID orig_id , dest_id ;
int latch ;
VertexID * orig_idp = 0 , * dest_idp = 0 ;
2013-03-03 00:40:24 +01:00
int * latchp = 0 ;
2010-01-04 09:27:50 +01:00
open_query : : row row ;
bmove_align ( buf , table - > s - > default_values , table - > s - > reclength ) ;
key_restore ( buf , ( byte * ) key , key_info , key_len ) ;
my_bitmap_map * old_map = dbug_tmp_use_all_columns ( table , table - > read_set ) ;
my_ptrdiff_t ptrdiff = buf - table - > record [ 0 ] ;
if ( ptrdiff )
{
2012-10-20 18:26:56 +02:00
field [ 0 ] - > move_field_offset ( ptrdiff ) ;
field [ 1 ] - > move_field_offset ( ptrdiff ) ;
field [ 2 ] - > move_field_offset ( ptrdiff ) ;
2010-01-04 09:27:50 +01:00
}
2013-03-08 13:56:26 +01:00
String latchFieldValue ;
2010-01-04 09:27:50 +01:00
if ( ! field [ 0 ] - > is_null ( ) )
{
2013-03-04 13:45:29 +01:00
# ifdef RETAIN_INT_LATCH_COMPATIBILITY
if ( field [ 0 ] - > type ( ) = = MYSQL_TYPE_SHORT ) {
latch = ( int ) field [ 0 ] - > val_int ( ) ;
} else
# endif
{
2013-03-08 13:56:26 +01:00
field [ 0 ] - > val_str ( & latchFieldValue , & latchFieldValue ) ;
if ( ! parse_latch_string_to_legacy_int ( latchFieldValue , latch ) ) {
2013-03-04 13:45:29 +01:00
// Invalid, so warn & fail
push_warning_printf ( current_thd , MYSQL_ERROR : : WARN_LEVEL_WARN , ER_WRONG_ARGUMENTS , ER ( ER_WRONG_ARGUMENTS ) , " OQGRAPH latch " ) ;
table - > status = STATUS_NOT_FOUND ;
2013-05-31 11:59:23 +02:00
if ( ptrdiff ) /* fixes debug build assert - should be a tidier way to do this */
{
field [ 0 ] - > move_field_offset ( - ptrdiff ) ;
field [ 1 ] - > move_field_offset ( - ptrdiff ) ;
field [ 2 ] - > move_field_offset ( - ptrdiff ) ;
}
dbug_tmp_restore_column_map ( table - > read_set , old_map ) ;
2013-03-04 13:45:29 +01:00
return error_code ( oqgraph : : NO_MORE_DATA ) ;
}
2013-03-02 13:44:27 +01:00
}
2010-01-04 09:27:50 +01:00
latchp = & latch ;
}
if ( ! field [ 1 ] - > is_null ( ) )
{
orig_id = ( VertexID ) field [ 1 ] - > val_int ( ) ;
orig_idp = & orig_id ;
}
if ( ! field [ 2 ] - > is_null ( ) )
{
dest_id = ( VertexID ) field [ 2 ] - > val_int ( ) ;
dest_idp = & dest_id ;
}
if ( ptrdiff )
{
2012-10-20 18:26:56 +02:00
field [ 0 ] - > move_field_offset ( - ptrdiff ) ;
field [ 1 ] - > move_field_offset ( - ptrdiff ) ;
field [ 2 ] - > move_field_offset ( - ptrdiff ) ;
2010-01-04 09:27:50 +01:00
}
dbug_tmp_restore_column_map ( table - > read_set , old_map ) ;
2013-03-08 13:56:26 +01:00
// Keep the latch around so we can use it in the query result later -
// See fill_record().
// at the moment our best option is to associate it with the graph
// so we pass the string now.
// In the future we should refactor parse_latch_string_to_legacy_int()
// into oqgraph instead.
2013-08-10 12:43:20 +02:00
if ( latchp )
graph - > retainLatchFieldValue ( latchFieldValue . c_ptr_safe ( ) ) ;
else
graph - > retainLatchFieldValue ( NULL ) ;
2013-03-08 13:56:26 +01:00
2013-03-07 14:29:21 +01:00
DBUG_PRINT ( " oq-debug " , ( " index_read_idx ::>> search(latch:%s,%ld,%ld) " ,
latchToCode ( latch ) , orig_idp ? ( long ) * orig_idp : - 1 , dest_idp ? ( long ) * dest_idp : - 1 ) ) ;
2010-01-04 09:27:50 +01:00
res = graph - > search ( latchp , orig_idp , dest_idp ) ;
2013-03-07 14:29:21 +01:00
DBUG_PRINT ( " oq-debug " , ( " search() = %d " , res ) ) ;
2013-03-08 13:56:26 +01:00
if ( ! res & & ! ( res = graph - > fetch_row ( row ) ) ) {
2010-01-04 09:27:50 +01:00
res = fill_record ( buf , row ) ;
2013-03-08 13:56:26 +01:00
}
2010-01-04 09:27:50 +01:00
table - > status = res ? STATUS_NOT_FOUND : 0 ;
return error_code ( res ) ;
}
int ha_oqgraph : : fill_record ( byte * record , const open_query : : row & row )
{
Field * * field = table - > field ;
bmove_align ( record , table - > s - > default_values , table - > s - > reclength ) ;
my_bitmap_map * old_map = dbug_tmp_use_all_columns ( table , table - > write_set ) ;
my_ptrdiff_t ptrdiff = record - table - > record [ 0 ] ;
if ( ptrdiff )
{
2012-10-20 18:26:56 +02:00
field [ 0 ] - > move_field_offset ( ptrdiff ) ;
field [ 1 ] - > move_field_offset ( ptrdiff ) ;
field [ 2 ] - > move_field_offset ( ptrdiff ) ;
field [ 3 ] - > move_field_offset ( ptrdiff ) ;
field [ 4 ] - > move_field_offset ( ptrdiff ) ;
field [ 5 ] - > move_field_offset ( ptrdiff ) ;
2010-01-04 09:27:50 +01:00
}
2013-03-07 14:29:21 +01:00
DBUG_PRINT ( " oq-debug " , ( " fill_record() ::>> %s,%ld,%ld,%lf,%ld,%ld " ,
row . latch_indicator ? latchToCode ( ( int ) row . latch ) : " - " ,
row . orig_indicator ? ( long ) row . orig : - 1 ,
row . dest_indicator ? ( long ) row . dest : - 1 ,
row . weight_indicator ? ( double ) row . weight : - 1 ,
row . seq_indicator ? ( long ) row . seq : - 1 ,
row . link_indicator ? ( long ) row . link : - 1 ) ) ;
2010-01-04 09:27:50 +01:00
// just each field specifically, no sense iterating
if ( row . latch_indicator )
{
field [ 0 ] - > set_notnull ( ) ;
2013-03-07 14:29:21 +01:00
// Convert the latch back to a varchar32
if ( field [ 0 ] - > type ( ) = = MYSQL_TYPE_VARCHAR ) {
2013-03-08 13:56:26 +01:00
field [ 0 ] - > store ( row . latchStringValue , row . latchStringValueLen , & my_charset_latin1 ) ;
2013-03-07 14:29:21 +01:00
}
# ifdef RETAIN_INT_LATCH_COMPATIBILITY
else if ( field [ 0 ] - > type ( ) = = MYSQL_TYPE_SHORT ) {
field [ 0 ] - > store ( ( longlong ) row . latch , 0 ) ;
}
# endif
2010-01-04 09:27:50 +01:00
}
if ( row . orig_indicator )
{
field [ 1 ] - > set_notnull ( ) ;
2010-11-30 00:27:14 +01:00
field [ 1 ] - > store ( ( longlong ) row . orig , 0 ) ;
2010-01-04 09:27:50 +01:00
}
if ( row . dest_indicator )
{
field [ 2 ] - > set_notnull ( ) ;
2010-11-30 00:27:14 +01:00
field [ 2 ] - > store ( ( longlong ) row . dest , 0 ) ;
2010-01-04 09:27:50 +01:00
}
if ( row . weight_indicator )
{
field [ 3 ] - > set_notnull ( ) ;
field [ 3 ] - > store ( ( double ) row . weight ) ;
}
if ( row . seq_indicator )
{
field [ 4 ] - > set_notnull ( ) ;
2010-12-04 11:35:41 +01:00
field [ 4 ] - > store ( ( longlong ) row . seq , 0 ) ;
2010-01-04 09:27:50 +01:00
}
if ( row . link_indicator )
{
field [ 5 ] - > set_notnull ( ) ;
2010-11-30 00:27:14 +01:00
field [ 5 ] - > store ( ( longlong ) row . link , 0 ) ;
2010-01-04 09:27:50 +01:00
}
if ( ptrdiff )
{
2012-10-20 18:26:56 +02:00
field [ 0 ] - > move_field_offset ( - ptrdiff ) ;
field [ 1 ] - > move_field_offset ( - ptrdiff ) ;
field [ 2 ] - > move_field_offset ( - ptrdiff ) ;
field [ 3 ] - > move_field_offset ( - ptrdiff ) ;
field [ 4 ] - > move_field_offset ( - ptrdiff ) ;
field [ 5 ] - > move_field_offset ( - ptrdiff ) ;
2010-01-04 09:27:50 +01:00
}
dbug_tmp_restore_column_map ( table - > write_set , old_map ) ;
return 0 ;
}
int ha_oqgraph : : rnd_init ( bool scan )
{
2011-04-13 00:50:32 +02:00
edges - > prepare_for_position ( ) ;
2010-01-04 09:27:50 +01:00
return error_code ( graph - > random ( scan ) ) ;
}
int ha_oqgraph : : rnd_next ( byte * buf )
{
int res ;
open_query : : row row ;
2013-06-29 15:26:29 +02:00
if ( ! ( res = graph - > fetch_row ( row ) ) ) // FIXME - this called after DELETE FROM graph_base; hangs...
2010-01-04 09:27:50 +01:00
res = fill_record ( buf , row ) ;
table - > status = res ? STATUS_NOT_FOUND : 0 ;
return error_code ( res ) ;
}
int ha_oqgraph : : rnd_pos ( byte * buf , byte * pos )
{
int res ;
open_query : : row row ;
if ( ! ( res = graph - > fetch_row ( row , pos ) ) )
res = fill_record ( buf , row ) ;
table - > status = res ? STATUS_NOT_FOUND : 0 ;
return error_code ( res ) ;
}
void ha_oqgraph : : position ( const byte * record )
{
graph - > row_ref ( ( void * ) ref ) ; // Ref is aligned
}
int ha_oqgraph : : cmp_ref ( const byte * ref1 , const byte * ref2 )
{
return memcmp ( ref1 , ref2 , oqgraph : : sizeof_ref ) ;
}
int ha_oqgraph : : info ( uint flag )
{
2011-04-13 00:50:32 +02:00
RECORDS = graph - > edges_count ( ) ;
2010-01-04 09:27:50 +01:00
/*
If info ( ) is called for the first time after open ( ) , we will still
have to update the key statistics . Hoping that a table lock is now
in place .
*/
2011-04-13 00:50:32 +02:00
// if (key_stat_version != share->key_stat_version)
// update_key_stats();
2010-01-04 09:27:50 +01:00
return 0 ;
}
int ha_oqgraph : : extra ( enum ha_extra_function operation )
{
2011-04-13 00:50:32 +02:00
return edges - > file - > extra ( operation ) ;
2010-01-04 09:27:50 +01:00
}
int ha_oqgraph : : delete_all_rows ( )
{
2013-02-11 01:07:55 +01:00
return HA_ERR_TABLE_READONLY ;
2010-01-04 09:27:50 +01:00
}
int ha_oqgraph : : external_lock ( THD * thd , int lock_type )
{
2013-09-10 15:14:45 +02:00
// This method is also called to _unlock_ (lock_type == F_UNLCK)
// Which means we need to release things before we let the underlying backing table lock go...
if ( lock_type = = F_UNLCK ) {
// If we have an index open on the backing table, we need to close it out here
// this means destroying any open cursor first.
// Then we can let the unlock go through to the backing table
graph - > release_cursor ( ) ;
}
2011-04-13 00:50:32 +02:00
return edges - > file - > ha_external_lock ( thd , lock_type ) ;
2010-01-04 09:27:50 +01:00
}
THR_LOCK_DATA * * ha_oqgraph : : store_lock ( THD * thd ,
THR_LOCK_DATA * * to ,
enum thr_lock_type lock_type )
{
2011-04-13 00:50:32 +02:00
return edges - > file - > store_lock ( thd , to , lock_type ) ;
2010-01-04 09:27:50 +01:00
}
/*
We have to ignore ENOENT entries as the HEAP table is created on open and
not when doing a CREATE on the table .
*/
2011-04-13 00:50:32 +02:00
int ha_oqgraph : : delete_table ( const char * )
2010-01-04 09:27:50 +01:00
{
2011-04-13 00:50:32 +02:00
return 0 ;
2010-01-04 09:27:50 +01:00
}
2011-04-13 00:50:32 +02:00
int ha_oqgraph : : rename_table ( const char * , const char * )
2010-01-04 09:27:50 +01:00
{
return 0 ;
}
ha_rows ha_oqgraph : : records_in_range ( uint inx , key_range * min_key ,
key_range * max_key )
{
KEY * key = table - > key_info + inx ;
2013-03-07 14:29:21 +01:00
# ifdef VERBOSE_DEBUG
{
String temp ;
key - > key_part [ 0 ] . field - > val_str ( & temp ) ;
temp . c_ptr_safe ( ) ;
DBUG_PRINT ( " oq-debug " , ( " records_in_range ::>> inx=%u " , inx ) ) ;
DBUG_PRINT ( " oq-debug " , ( " records_in_range ::>> key0=%s. " , temp . c_ptr ( ) ) ) ; // for some reason when I had ...inx=%u key=%s", inx, temp.c_ptr_safe()) it printed nothing ...
}
# endif
2010-01-04 09:27:50 +01:00
if ( ! min_key | | ! max_key | |
min_key - > length ! = max_key - > length | |
min_key - > length < key - > key_length - key - > key_part [ 2 ] . store_length | |
min_key - > flag ! = HA_READ_KEY_EXACT | |
max_key - > flag ! = HA_READ_AFTER_KEY )
{
2013-08-10 12:43:20 +02:00
if ( min_key - > length = = key - > key_part [ 0 ] . store_length & & ! key - > key_part [ 0 ] . field - > is_null ( ) ) /* ensure select * from x where latch is null is consistent with no latch */
2010-01-04 09:27:50 +01:00
{
2013-03-07 14:29:21 +01:00
// If latch is not null and equals 0, return # nodes
// How to decode the key, For VARCHAR(32), from empirical observation using the debugger
// and information gleaned from:
// http://grokbase.com/t/mysql/internals/095h6ch1q7/parsing-key-information
// http://dev.mysql.com/doc/internals/en/support-for-indexing.html#parsing-key-information
// comments in opt_range.cc
// POSSIBLY ONLY VALID FOR INNODB!
// For a the following query:
// SELECT * FROM graph2 WHERE latch = 'breadth_first' AND origid = 123 AND weight = 1;
// key->key_part[0].field->ptr is the value of latch, which is a 1-byte string length followed by the value ('breadth_first')
// key->key_part[2].field->ptr is the value of origid (123)
// key->key_part[1].field->ptr is the value of destid which is not specified in the query so we ignore it in this case
// so given this ordering we seem to be using the second key specified in create table (aka KEY (latch, destid, origid) USING HASH ))
// min_key->key[0] is the 'null' bit and contains 0 in this instance
// min_key->key[1..2] seems to be 16-bit string length
// min_key->key[3..34] hold the varchar(32) value which is that specified in the query
// min_key->key[35] is the null bit of origid
// min_key->key[36..43] is the value in the query (123)
// max_key->key[0] is the ;null' bit and contains 0 in this instance
// max_key->key[1..2] seems to be 16-bit string length
// max_key->key[3..34] hold the varchar(32) value which is that specified in the query
// max_key->key[35] is the null bit of origid
// max_key->key[36..43] is the value in the query (123)
// But after knowing all that, all we care about is the latch value
2013-03-04 13:45:29 +01:00
2013-03-07 14:29:21 +01:00
// First draft - ignore most of the stuff, but will likely break if query altered
2013-03-04 13:45:29 +01:00
2013-03-07 14:29:21 +01:00
// It turns out there is a better way though, to access the string,
// as demonstrated in key_unpack() of sql/key.cc
String latchCode ;
int latch = - 1 ;
if ( key - > key_part [ 0 ] . field - > type ( ) = = MYSQL_TYPE_VARCHAR ) {
key - > key_part [ 0 ] . field - > val_str ( & latchCode ) ;
parse_latch_string_to_legacy_int ( latchCode , latch ) ;
}
// what if someone did something dumb, like mismatching the latches?
# ifdef RETAIN_INT_LATCH_COMPATIBILITY
else if ( key - > key_part [ 0 ] . field - > type ( ) = = MYSQL_TYPE_SHORT ) {
// If not null, and zero ...
// Note, the following code relies on the fact that the three bytes
// at beginning of min_key just happen to be the null indicator and the
// 16-bit value of the latch ...
// this will fall through if the user alter-tabled to not null
if ( key - > key_part [ 0 ] . null_bit & & ! min_key - > key [ 0 ] & &
! min_key - > key [ 1 ] & & ! min_key - > key [ 2 ] ) {
latch = oqgraph : : NO_SEARCH ;
}
}
# endif
if ( latch ! = oqgraph : : NO_SEARCH ) {
// Invalid key type...
// Don't assert, in case the user used alter table on us
return HA_POS_ERROR ; // Can only use exact keys
}
2013-03-08 13:56:26 +01:00
unsigned N = graph - > vertices_count ( ) ;
DBUG_PRINT ( " oq-debug " , ( " records_in_range ::>> N=%u (vertices) " , N ) ) ;
return N ;
2010-01-04 09:27:50 +01:00
}
return HA_POS_ERROR ; // Can only use exact keys
}
2013-03-08 13:56:26 +01:00
if ( stats . records < = 1 ) {
2013-05-19 14:04:42 +02:00
DBUG_PRINT ( " oq-debug " , ( " records_in_range ::>> N=%u (stats) " , ( unsigned ) stats . records ) ) ;
2012-10-20 18:26:56 +02:00
return stats . records ;
2013-03-08 13:56:26 +01:00
}
2010-01-04 09:27:50 +01:00
/* Assert that info() did run. We need current statistics here. */
2011-04-13 00:50:32 +02:00
//DBUG_ASSERT(key_stat_version == share->key_stat_version);
2013-09-07 11:21:05 +02:00
//ha_rows result= key->rec_per_key[key->user_defined_key_parts-1];
2011-04-13 00:50:32 +02:00
ha_rows result = 10 ;
2013-05-19 14:04:42 +02:00
DBUG_PRINT ( " oq-debug " , ( " records_in_range ::>> N=%u " , ( unsigned ) result ) ) ;
2010-01-04 09:27:50 +01:00
return result ;
}
int ha_oqgraph : : create ( const char * name , TABLE * table_arg ,
HA_CREATE_INFO * create_info )
{
2011-04-13 00:50:32 +02:00
oqgraph_table_option_struct * options =
2013-02-11 01:07:55 +01:00
reinterpret_cast < oqgraph_table_option_struct * > ( table_arg - > s - > option_struct ) ;
2010-01-04 09:27:50 +01:00
2013-03-04 13:45:29 +01:00
DBUG_ENTER ( " ha_oqgraph::create " ) ;
2013-02-27 13:41:57 +01:00
DBUG_PRINT ( " oq-debug " , ( " create(name=%s) " , name ) ) ;
2013-03-07 14:29:21 +01:00
if ( oqgraph_check_table_structure ( table_arg ) ) {
2013-03-04 13:45:29 +01:00
DBUG_RETURN ( HA_WRONG_CREATE_OPTION ) ;
}
2010-01-04 09:27:50 +01:00
2011-05-16 07:22:58 +02:00
( void ) ( options ) ;
2013-03-04 13:45:29 +01:00
DBUG_RETURN ( 0 ) ;
2010-01-04 09:27:50 +01:00
}
void ha_oqgraph : : update_create_info ( HA_CREATE_INFO * create_info )
{
table - > file - > info ( HA_STATUS_AUTO ) ;
}
struct st_mysql_storage_engine oqgraph_storage_engine =
{ MYSQL_HANDLERTON_INTERFACE_VERSION } ;
2013-02-21 19:36:25 +01:00
extern " C " const char * const oqgraph_boost_version ;
extern " C " const char * const oqgraph_judy_version ;
static struct st_mysql_show_var oqgraph_status [ ] =
{
{ " OQGraph_Boost_Version " , ( char * ) & oqgraph_boost_version , SHOW_CHAR_PTR } ,
/*{ "OQGraph_Judy_Version", (char*) &oqgraph_judy_version, SHOW_CHAR_PTR },*/
2013-02-28 06:49:53 +01:00
{ 0 , 0 , SHOW_UNDEF }
2013-02-21 19:36:25 +01:00
} ;
2013-05-19 14:13:43 +02:00
# ifdef RETAIN_INT_LATCH_COMPATIBILITY
2013-05-19 14:04:42 +02:00
static struct st_mysql_sys_var * oqgraph_sysvars [ ] = {
MYSQL_SYSVAR ( allow_create_integer_latch ) ,
0
} ;
2013-05-19 14:13:43 +02:00
# endif
2011-04-25 17:22:25 +02:00
maria_declare_plugin ( oqgraph )
2010-01-04 09:27:50 +01:00
{
MYSQL_STORAGE_ENGINE_PLUGIN ,
& oqgraph_storage_engine ,
" OQGRAPH " ,
" Arjen Lentz & Antony T Curtis, Open Query " ,
oqgraph_description ,
PLUGIN_LICENSE_GPL ,
( int ( * ) ( void * ) ) oqgraph_init , /* Plugin Init */
oqgraph_fini , /* Plugin Deinit */
2011-04-13 00:50:32 +02:00
0x0300 , /* Version: 3s.0 */
2013-02-21 19:36:25 +01:00
oqgraph_status , /* status variables */
2013-05-19 14:13:43 +02:00
# ifdef RETAIN_INT_LATCH_COMPATIBILITY
2013-05-19 14:04:42 +02:00
oqgraph_sysvars , /* system variables */
2013-05-19 14:13:43 +02:00
# else
NULL ,
# endif
2013-02-11 01:21:49 +01:00
" 3.0 " ,
2011-04-25 17:22:25 +02:00
MariaDB_PLUGIN_MATURITY_BETA
2010-01-04 09:27:50 +01:00
}
2011-04-25 17:22:25 +02:00
maria_declare_plugin_end ;