2020-06-12 06:03:00 +00:00
/*
* $ Id $
*
Copyright ( c ) 2016 - 2020 Chung , Hyung - Hwan . All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions
are met :
1. Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE AUTHOR " AS IS " AND ANY EXPRESS OR
IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WAfRRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
2020-06-14 16:09:17 +00:00
# include <mio-mar.h>
2020-06-12 06:03:00 +00:00
# include "mio-prv.h"
# include <mariadb/mysql.h>
/* ========================================================================= */
2020-06-14 16:09:17 +00:00
static int dev_mar_make ( mio_dev_t * dev , void * ctx )
2020-06-12 06:03:00 +00:00
{
mio_t * mio = dev - > mio ;
2020-06-14 16:09:17 +00:00
mio_dev_mar_t * rdev = ( mio_dev_mar_t * ) dev ;
mio_dev_mar_make_t * mi = ( mio_dev_mar_make_t * ) ctx ;
2020-06-12 06:03:00 +00:00
rdev - > hnd = mysql_init ( MIO_NULL ) ;
if ( MIO_UNLIKELY ( ! rdev - > hnd ) )
{
mio_seterrnum ( mio , MIO_ESYSMEM ) ;
return - 1 ;
}
if ( mysql_options ( rdev - > hnd , MYSQL_OPT_NONBLOCK , 0 ) ! = 0 )
{
mio_seterrbfmt ( mio , MIO_ESYSERR , " %s " , mysql_error ( rdev - > hnd ) ) ;
mysql_close ( rdev - > hnd ) ;
rdev - > hnd = MIO_NULL ;
return - 1 ;
}
{
my_bool x = 0 ;
mysql_options ( rdev - > hnd , MYSQL_OPT_RECONNECT , & x ) ;
}
rdev - > dev_cap = MIO_DEV_CAP_IN | MIO_DEV_CAP_OUT | MIO_DEV_CAP_VIRTUAL ; /* mysql_init() doesn't create a socket. so no IO is possible at this point */
rdev - > on_read = mi - > on_read ;
rdev - > on_write = mi - > on_write ;
rdev - > on_connect = mi - > on_connect ;
rdev - > on_disconnect = mi - > on_disconnect ;
rdev - > on_query_started = mi - > on_query_started ;
rdev - > on_row_fetched = mi - > on_row_fetched ;
return 0 ;
}
2020-06-14 16:09:17 +00:00
static int dev_mar_kill ( mio_dev_t * dev , int force )
2020-06-12 06:03:00 +00:00
{
/*mio_t* mio = dev->mio;*/
2020-06-14 16:09:17 +00:00
mio_dev_mar_t * rdev = ( mio_dev_mar_t * ) dev ;
2020-06-12 06:03:00 +00:00
if ( rdev - > on_disconnect ) rdev - > on_disconnect ( rdev ) ;
2020-06-12 07:54:00 +00:00
if ( rdev - > res )
{
mysql_free_result ( rdev - > res ) ;
rdev - > res = MIO_NULL ;
}
2020-06-12 06:03:00 +00:00
if ( rdev - > hnd )
{
mysql_close ( rdev - > hnd ) ;
rdev - > hnd = MIO_NULL ;
}
return 0 ;
}
2020-06-14 16:09:17 +00:00
static mio_syshnd_t dev_mar_getsyshnd ( mio_dev_t * dev )
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
mio_dev_mar_t * rdev = ( mio_dev_mar_t * ) dev ;
2020-06-12 06:03:00 +00:00
return ( mio_syshnd_t ) mysql_get_socket ( rdev - > hnd ) ;
}
static int events_to_mysql_wstatus ( int events )
{
int wstatus = 0 ;
if ( events & MIO_DEV_EVENT_IN ) wstatus | = MYSQL_WAIT_READ ;
if ( events & MIO_DEV_EVENT_OUT ) wstatus | = MYSQL_WAIT_WRITE ;
if ( events & MIO_DEV_EVENT_PRI ) wstatus | = MYSQL_WAIT_EXCEPT ;
return wstatus ;
}
static int mysql_wstatus_to_events ( int wstatus )
{
int events = 0 ;
if ( wstatus & MYSQL_WAIT_READ ) events | = MIO_DEV_EVENT_IN ;
if ( wstatus & MYSQL_WAIT_WRITE ) events | = MIO_DEV_EVENT_OUT ;
if ( wstatus & MYSQL_WAIT_EXCEPT ) events | = MIO_DEV_EVENT_PRI ;
/* TODO: wstatus& MYSQL_WAIT_TIMEOUT? */
return events ;
}
2020-06-14 16:09:17 +00:00
static MIO_INLINE void watch_mysql ( mio_dev_mar_t * rdev , int wstatus )
2020-06-12 06:03:00 +00:00
{
if ( mio_dev_watch ( ( mio_dev_t * ) rdev , MIO_DEV_WATCH_UPDATE , mysql_wstatus_to_events ( wstatus ) ) < = - 1 )
{
/* watcher update failure. it's critical */
mio_stop ( rdev - > mio , MIO_STOPREQ_WATCHER_ERROR ) ;
}
}
2020-06-14 16:09:17 +00:00
static void start_fetch_row ( mio_dev_mar_t * rdev )
2020-06-12 06:03:00 +00:00
{
MYSQL_ROW row ;
int status ;
status = mysql_fetch_row_start ( & row , rdev - > res ) ;
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_ROW_FETCHING ) ;
2020-06-12 06:03:00 +00:00
if ( status )
{
/* row fetched */
rdev - > row_fetched = 0 ;
watch_mysql ( rdev , status ) ;
}
else
{
/* row fetched - don't handle it immediately here */
rdev - > row_fetched = 1 ;
rdev - > row_wstatus = status ;
rdev - > row = row ;
watch_mysql ( rdev , MYSQL_WAIT_READ | MYSQL_WAIT_WRITE ) ;
}
}
2020-06-14 16:09:17 +00:00
static int dev_mar_ioctl ( mio_dev_t * dev , int cmd , void * arg )
2020-06-12 06:03:00 +00:00
{
mio_t * mio = dev - > mio ;
2020-06-14 16:09:17 +00:00
mio_dev_mar_t * rdev = ( mio_dev_mar_t * ) dev ;
2020-06-12 06:03:00 +00:00
switch ( cmd )
{
2020-06-14 16:09:17 +00:00
case MIO_DEV_MAR_CONNECT :
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
mio_dev_mar_connect_t * ci = ( mio_dev_mar_connect_t * ) arg ;
2020-06-12 06:03:00 +00:00
MYSQL * ret ;
int status ;
2020-06-14 16:09:17 +00:00
if ( MIO_DEV_MAR_GET_PROGRESS ( rdev ) )
2020-06-12 06:03:00 +00:00
{
/* can't connect again */
mio_seterrbfmt ( mio , MIO_EPERM , " operation in progress. disallowed to connect again " ) ;
return - 1 ;
}
status = mysql_real_connect_start ( & ret , rdev - > hnd , ci - > host , ci - > username , ci - > password , ci - > dbname , ci - > port , MIO_NULL , 0 ) ;
rdev - > dev_cap & = ~ MIO_DEV_CAP_VIRTUAL ; /* a socket is created in mysql_real_connect_start() */
if ( status )
{
/* not connected */
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_CONNECTING ) ;
2020-06-12 06:03:00 +00:00
rdev - > connected = 0 ;
watch_mysql ( rdev , status ) ;
}
else
{
/* connected immediately */
if ( MIO_UNLIKELY ( ! ret ) )
{
mio_seterrbfmt ( mio , MIO_ESYSERR , " %s " , mysql_error ( rdev - > hnd ) ) ;
return - 1 ;
}
/* regiter it in the multiplexer so that the ready() handler is
* invoked to call the on_connect ( ) callback */
rdev - > connected = 1 ;
watch_mysql ( rdev , MYSQL_WAIT_READ | MYSQL_WAIT_WRITE ) ; /* TODO: verify this */
}
return 0 ;
}
2020-06-14 16:09:17 +00:00
case MIO_DEV_MAR_QUERY_WITH_BCS :
2020-06-12 06:03:00 +00:00
{
const mio_bcs_t * qstr = ( const mio_bcs_t * ) arg ;
int err , status ;
if ( rdev - > res ) /* TODO: more accurate check */
{
mio_seterrbfmt ( mio , MIO_EPERM , " operation in progress. disallowed to query again " ) ;
return - 1 ;
}
status = mysql_real_query_start ( & err , rdev - > hnd , qstr - > ptr , qstr - > len ) ;
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_QUERY_STARTING ) ;
2020-06-12 06:03:00 +00:00
if ( status )
{
/* not done */
rdev - > query_started = 0 ;
watch_mysql ( rdev , status ) ;
}
else
{
/* query sent immediately */
2020-06-21 08:42:36 +00:00
if ( err )
{
if ( err = = 1 ) err = mysql_errno ( rdev - > hnd ) ;
mio_copy_bcstr ( rdev - > errbuf , MIO_COUNTOF ( rdev - > errbuf ) , mysql_error ( rdev - > hnd ) ) ;
}
2020-06-12 06:03:00 +00:00
rdev - > query_started = 1 ;
rdev - > query_ret = err ;
watch_mysql ( rdev , MYSQL_WAIT_READ | MYSQL_WAIT_WRITE ) ;
}
return 0 ;
}
2020-06-14 16:09:17 +00:00
case MIO_DEV_MAR_FETCH_ROW :
2020-06-12 06:03:00 +00:00
{
if ( ! rdev - > res )
{
rdev - > res = mysql_use_result ( rdev - > hnd ) ;
if ( MIO_UNLIKELY ( ! rdev - > res ) )
{
mio_seterrbfmt ( mio , MIO_ESYSERR , " %s " , mysql_error ( rdev - > hnd ) ) ;
return - 1 ;
}
}
start_fetch_row ( rdev ) ;
return 0 ;
}
default :
mio_seterrnum ( mio , MIO_EINVAL ) ;
return - 1 ;
}
}
2020-06-14 16:09:17 +00:00
static mio_dev_mth_t dev_mar_methods =
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
dev_mar_make ,
dev_mar_kill ,
dev_mar_getsyshnd ,
2020-06-12 06:03:00 +00:00
MIO_NULL ,
MIO_NULL ,
MIO_NULL ,
2020-06-14 16:09:17 +00:00
dev_mar_ioctl
2020-06-12 06:03:00 +00:00
} ;
/* ========================================================================= */
2020-06-14 16:09:17 +00:00
static int dev_evcb_mar_ready ( mio_dev_t * dev , int events )
2020-06-12 06:03:00 +00:00
{
mio_t * mio = dev - > mio ;
2020-06-14 16:09:17 +00:00
mio_dev_mar_t * rdev = ( mio_dev_mar_t * ) dev ;
2020-06-12 06:03:00 +00:00
#if 0
if ( events & MIO_DEV_EVENT_ERR )
{
int errcode ;
mio_scklen_t len ;
len = MIO_SIZEOF ( errcode ) ;
if ( getsockopt ( mysql_get_socket ( rdev - > hnd ) , SOL_SOCKET , SO_ERROR , ( char * ) & errcode , & len ) = = - 1 )
{
/* the error number is set to the socket error code.
* errno resulting from getsockopt ( ) doesn ' t reflect the actual
* socket error . so errno is not used to set the error number .
* instead , the generic device error MIO_EDEVERRR is used */
mio_seterrbfmt ( mio , MIO_EDEVERR , " device error - unable to get SO_ERROR " ) ;
}
else
{
mio_seterrwithsyserr ( mio , 0 , errcode ) ;
}
return - 1 ;
}
# endif
2020-06-14 16:09:17 +00:00
switch ( MIO_DEV_MAR_GET_PROGRESS ( rdev ) )
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
case MIO_DEV_MAR_CONNECTING :
2020-06-12 06:03:00 +00:00
if ( rdev - > connected )
{
rdev - > connected = 0 ;
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_CONNECTED ) ;
2020-06-12 06:03:00 +00:00
if ( rdev - > on_connect ) rdev - > on_connect ( rdev ) ;
}
else
{
int status ;
MYSQL * tmp ;
status = mysql_real_connect_cont ( & tmp , rdev - > hnd , events_to_mysql_wstatus ( events ) ) ;
watch_mysql ( rdev , status ) ;
if ( ! status )
{
/* connected */
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_CONNECTED ) ;
2020-06-12 06:03:00 +00:00
if ( rdev - > on_connect ) rdev - > on_connect ( rdev ) ;
}
}
break ;
2020-06-14 16:09:17 +00:00
case MIO_DEV_MAR_QUERY_STARTING :
2020-06-12 06:03:00 +00:00
if ( rdev - > query_started )
{
rdev - > query_started = 0 ;
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_QUERY_STARTED ) ;
2020-06-21 08:42:36 +00:00
if ( rdev - > on_query_started ) rdev - > on_query_started ( rdev , rdev - > query_ret , ( rdev - > query_ret ? rdev - > errbuf : MIO_NULL ) ) ;
2020-06-12 06:03:00 +00:00
}
else
{
int status ;
int tmp ;
status = mysql_real_query_cont ( & tmp , rdev - > hnd , events_to_mysql_wstatus ( events ) ) ;
watch_mysql ( rdev , status ) ;
if ( ! status )
{
/* query sent */
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_QUERY_STARTED ) ;
2020-06-21 08:42:36 +00:00
if ( tmp = = 1 ) tmp = mysql_errno ( rdev - > hnd ) ; /* tmp is set to 1 by mariadb-connector-c 3.1 as of this writing. let me work around it by fetching the error code */
if ( rdev - > on_query_started ) rdev - > on_query_started ( rdev , tmp , ( tmp ? mysql_error ( rdev - > hnd ) : MIO_NULL ) ) ;
2020-06-12 06:03:00 +00:00
}
}
break ;
2020-06-14 16:09:17 +00:00
case MIO_DEV_MAR_ROW_FETCHING :
2020-06-12 06:03:00 +00:00
{
int status ;
MYSQL_ROW row ;
if ( rdev - > row_fetched )
{
row = ( MYSQL_ROW ) rdev - > row ;
rdev - > row_fetched = 0 ;
if ( ! row )
{
MIO_ASSERT ( mio , rdev - > res ! = MIO_NULL ) ;
mysql_free_result ( rdev - > res ) ; /* this doesn't block after the last row */
rdev - > res = MIO_NULL ;
watch_mysql ( rdev , rdev - > row_wstatus ) ;
}
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_ROW_FETCHED ) ;
2020-06-12 06:03:00 +00:00
if ( rdev - > on_row_fetched ) rdev - > on_row_fetched ( rdev , row ) ;
if ( row ) start_fetch_row ( rdev ) ;
}
else
{
/* TODO: if rdev->res is MIO_NULL, error.. */
status = mysql_fetch_row_cont ( & row , rdev - > res , events_to_mysql_wstatus ( events ) ) ;
if ( ! status )
{
if ( ! row )
{
/* the last row has been received - cleanup before invoking the callback */
watch_mysql ( rdev , status ) ;
MIO_ASSERT ( mio , rdev - > res ! = MIO_NULL ) ;
mysql_free_result ( rdev - > res ) ; /* this doesn't block after the last row */
rdev - > res = MIO_NULL ;
}
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_ROW_FETCHED ) ;
2020-06-12 06:03:00 +00:00
if ( rdev - > on_row_fetched ) rdev - > on_row_fetched ( rdev , row ) ;
if ( row ) start_fetch_row ( rdev ) ; /* arrange to fetch the next row */
}
else
{
watch_mysql ( rdev , status ) ;
}
}
break ;
}
default :
2020-06-14 16:09:17 +00:00
mio_seterrbfmt ( mio , MIO_EINTERN , " invalid progress state in mar " ) ;
2020-06-12 06:03:00 +00:00
return - 1 ;
}
return 0 ; /* success. but skip core event handling */
}
2020-06-14 16:09:17 +00:00
static mio_dev_evcb_t dev_mar_event_callbacks =
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
dev_evcb_mar_ready ,
2020-06-12 06:03:00 +00:00
MIO_NULL , /* no read callback */
MIO_NULL /* no write callback */
} ;
/* ========================================================================= */
2020-06-14 16:09:17 +00:00
mio_dev_mar_t * mio_dev_mar_make ( mio_t * mio , mio_oow_t xtnsize , const mio_dev_mar_make_t * mi )
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
return ( mio_dev_mar_t * ) mio_dev_make (
mio , MIO_SIZEOF ( mio_dev_mar_t ) + xtnsize ,
& dev_mar_methods , & dev_mar_event_callbacks , ( void * ) mi ) ;
2020-06-12 06:03:00 +00:00
}
2020-06-14 16:09:17 +00:00
int mio_dev_mar_connect ( mio_dev_mar_t * dev , mio_dev_mar_connect_t * ci )
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
return mio_dev_ioctl ( ( mio_dev_t * ) dev , MIO_DEV_MAR_CONNECT , ci ) ;
2020-06-12 06:03:00 +00:00
}
2020-06-14 16:09:17 +00:00
int mio_dev_mar_querywithbchars ( mio_dev_mar_t * dev , const mio_bch_t * qstr , mio_oow_t qlen )
2020-06-12 06:03:00 +00:00
{
mio_bcs_t bcs = { ( mio_bch_t * ) qstr , qlen } ;
2020-06-14 16:09:17 +00:00
return mio_dev_ioctl ( ( mio_dev_t * ) dev , MIO_DEV_MAR_QUERY_WITH_BCS , & bcs ) ;
2020-06-12 06:03:00 +00:00
}
2020-06-14 16:09:17 +00:00
int mio_dev_mar_fetchrows ( mio_dev_mar_t * dev )
2020-06-12 06:03:00 +00:00
{
2020-06-14 16:09:17 +00:00
return mio_dev_ioctl ( ( mio_dev_t * ) dev , MIO_DEV_MAR_FETCH_ROW , MIO_NULL ) ;
2020-06-12 06:03:00 +00:00
}
2020-06-12 07:54:00 +00:00