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-24 03:00:46 +00:00
# include <mariadb/errmsg.h>
2020-06-24 07:37:11 +00:00
# include <sys/socket.h>
2020-06-12 06:03:00 +00:00
/* ========================================================================= */
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 )
{
2020-06-24 03:00:46 +00:00
mio_seterrbfmt ( mio , MIO_ESYSERR , " %hs " , mysql_error ( rdev - > hnd ) ) ;
2020-06-12 06:03:00 +00:00
mysql_close ( rdev - > hnd ) ;
rdev - > hnd = MIO_NULL ;
return - 1 ;
}
{
2020-06-24 03:00:46 +00:00
my_bool x = 0 ; /* no auto-reconnect */
2020-06-12 06:03:00 +00:00
mysql_options ( rdev - > hnd , MYSQL_OPT_RECONNECT , & x ) ;
}
2020-06-24 07:37:11 +00:00
#if 0
/* TOOD: timeout not implemented...
* timeout can ' t be implemented using the mysql timeout options in the nonblocking mode .
* i must create a timeer jobs for these */
if ( mi - > flags & MIO_DEV_MAR_USE_TMOUT )
{
unsigned int tmout ;
tmout = mi - > tmout . c . sec ; /* mysql supports the granularity of seconds only */
if ( tmout > = 0 ) mysql_options ( rdev - > hnd , MYSQL_OPT_CONNECT_TIMEOUT , & tmout ) ;
tmout = mi - > tmout . r . sec ;
if ( tmout > = 0 ) mysql_options ( rdev - > hnd , MYSQL_OPT_READ_TIMEOUT , & tmout ) ;
tmout = mi - > tmout . w . sec ;
if ( tmout > = 0 ) mysql_options ( rdev - > hnd , MYSQL_OPT_WRITE_TIMEOUT , & tmout ) ;
}
# endif
2020-06-12 06:03:00 +00:00
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 ;
2020-06-24 03:00:46 +00:00
rdev - > progress = MIO_DEV_MAR_INITIAL ;
2020-06-12 06:03:00 +00:00
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
2020-06-24 03:00:46 +00:00
/* if rdev->connected is 0 at this point,
* the underlying socket of this device is down */
2020-06-24 07:37:11 +00:00
if ( MIO_LIKELY ( rdev - > on_disconnect ) ) rdev - > on_disconnect ( rdev ) ;
/* hack */
if ( ! rdev - > broken )
{
/* mysql_free_result() blocks if not all rows have been read.
* mysql_close ( ) also blocks to transmit COM_QUIT ,
* in this context , it is not appropriate to call
* mysql_free_result_start ( ) / mysql_free_result_cont ( ) and
* mysql_close_start ( ) / mysql_close_cont ( ) .
* let me just call shutdown on the underlying socket to work around this issue .
* as a result , mysql_close ( ) will be unable to send COM_QUIT but will return fast
*/
shutdown ( mysql_get_socket ( rdev - > hnd ) , SHUT_RDWR ) ;
}
2020-06-12 06:03:00 +00:00
2020-06-12 07:54:00 +00:00
if ( rdev - > res )
{
mysql_free_result ( rdev - > res ) ;
rdev - > res = MIO_NULL ;
}
2020-06-24 07:37:11 +00:00
2020-06-12 06:03:00 +00:00
if ( rdev - > hnd )
{
2020-06-24 07:37:11 +00:00
mysql_close ( rdev - > hnd ) ;
2020-06-12 06:03:00 +00:00
rdev - > hnd = MIO_NULL ;
}
2020-06-24 03:00:46 +00:00
rdev - > connected = 0 ;
rdev - > broken = 0 ;
2020-06-12 06:03:00 +00:00
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-24 03:00:46 +00:00
if ( rdev - > broken ) return rdev - > broken_syshnd ; /* hack!! */
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-24 07:37:11 +00:00
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 )
{
2020-06-24 07:37:11 +00:00
/* row not fetched */
rdev - > row_fetched_deferred = 0 ;
2020-06-12 06:03:00 +00:00
watch_mysql ( rdev , status ) ;
}
else
{
/* row fetched - don't handle it immediately here */
2020-06-24 07:37:11 +00:00
rdev - > row_fetched_deferred = 1 ;
2020-06-12 06:03:00 +00:00
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-24 03:00:46 +00:00
MYSQL * tmp ;
2020-06-12 06:03:00 +00:00
int status ;
2020-06-24 03:00:46 +00:00
if ( MIO_DEV_MAR_GET_PROGRESS ( rdev ) ! = MIO_DEV_MAR_INITIAL )
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 ;
}
2020-06-24 03:00:46 +00:00
MIO_ASSERT ( mio , rdev - > connected_deferred = = 0 ) ;
status = mysql_real_connect_start ( & tmp , rdev - > hnd , ci - > host , ci - > username , ci - > password , ci - > dbname , ci - > port , MIO_NULL , 0 ) ;
2020-06-12 06:03:00 +00:00
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
watch_mysql ( rdev , status ) ;
}
else
{
2020-06-24 03:00:46 +00:00
if ( MIO_UNLIKELY ( ! tmp ) ) /* connection attempt failed immediately */
2020-06-12 06:03:00 +00:00
{
2020-06-24 03:00:46 +00:00
/* immediate failure doesn't invoke on_discoonect().
* the caller must check the return code of this function . */
mio_seterrbfmt ( mio , MIO_ESYSERR , " %hs " , mysql_error ( rdev - > hnd ) ) ;
2020-06-12 06:03:00 +00:00
return - 1 ;
}
2020-06-24 03:00:46 +00:00
/* connected_deferred immediately. postpone actual handling to the ready() callback */
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_CONNECTING ) ;
rdev - > connected_deferred = 1 ; /* to let the ready() handler to trigger on_connect() */
2020-06-12 06:03:00 +00:00
/* regiter it in the multiplexer so that the ready() handler is
* invoked to call the on_connect ( ) callback */
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 ;
2020-06-24 03:00:46 +00:00
mio_syshnd_t syshnd ;
if ( ! rdev - > connected )
{
mio_seterrbfmt ( mio , MIO_EPERM , " not connected. disallowed to query " ) ;
return - 1 ;
}
2020-06-12 06:03:00 +00:00
if ( rdev - > res ) /* TODO: more accurate check */
{
mio_seterrbfmt ( mio , MIO_EPERM , " operation in progress. disallowed to query again " ) ;
return - 1 ;
}
2020-06-24 03:00:46 +00:00
syshnd = mysql_get_socket ( rdev - > hnd ) ;
2020-06-12 06:03:00 +00:00
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 */
watch_mysql ( rdev , status ) ;
}
else
{
/* query sent immediately */
2020-06-21 08:42:36 +00:00
if ( err )
{
2020-06-24 03:00:46 +00:00
/* but there is an error */
2020-06-21 08:42:36 +00:00
if ( err = = 1 ) err = mysql_errno ( rdev - > hnd ) ;
2020-06-24 03:00:46 +00:00
mio_seterrbfmt ( mio , MIO_ESYSERR , " %hs " , mysql_error ( rdev - > hnd ) ) ;
if ( err = = CR_SERVER_LOST | | err = = CR_SERVER_GONE_ERROR )
{
/* the underlying socket must have gotten closed by mysql_real_query_start() */
const mio_ooch_t * prev_errmsg ;
prev_errmsg = mio_backuperrmsg ( mio ) ;
rdev - > broken = 1 ;
rdev - > broken_syshnd = syshnd ;
watch_mysql ( rdev , 0 ) ;
mio_dev_mar_halt ( rdev ) ; /* i can't keep this device alive regardless of the caller's post-action */
mio_seterrbfmt ( mio , MIO_ESYSERR , " %js " , prev_errmsg ) ;
}
return - 1 ;
2020-06-21 08:42:36 +00:00
}
2020-06-24 03:00:46 +00:00
/* sent without an error */
rdev - > query_started_deferred = 1 ;
2020-06-12 06:03:00 +00:00
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 ) )
{
2020-06-24 03:00:46 +00:00
mio_seterrbfmt ( mio , MIO_ESYSERR , " %hs " , mysql_error ( rdev - > hnd ) ) ;
2020-06-12 06:03:00 +00:00
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-24 03:00:46 +00:00
if ( rdev - > connected_deferred )
2020-06-12 06:03:00 +00:00
{
2020-06-24 03:00:46 +00:00
/* connection esablished dev_mar_ioctl() but postponed to this function */
rdev - > connected_deferred = 0 ;
rdev - > connected = 1 ; /* really 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 ) ;
}
else
{
int status ;
MYSQL * tmp ;
2020-06-24 03:00:46 +00:00
mio_syshnd_t syshnd ;
2020-06-12 06:03:00 +00:00
2020-06-24 03:00:46 +00:00
syshnd = mysql_get_socket ( rdev - > hnd ) ; /* ugly hack for handling a socket closed b y mysql_real_connect_cont() */
2020-06-12 06:03:00 +00:00
status = mysql_real_connect_cont ( & tmp , rdev - > hnd , events_to_mysql_wstatus ( events ) ) ;
2020-06-24 03:00:46 +00:00
if ( status )
2020-06-12 06:03:00 +00:00
{
2020-06-24 03:00:46 +00:00
/* connection in progress */
watch_mysql ( rdev , status ) ;
}
else
{
/* connection completed. */
if ( tmp )
{
/* established ok */
watch_mysql ( rdev , status ) ;
rdev - > connected = 1 ; /* really connected */
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_CONNECTED ) ;
if ( rdev - > on_connect ) rdev - > on_connect ( rdev ) ;
}
else
{
/* connection attempt failed */
rdev - > broken = 1 ; /* trick dev_mar_getsyshnd() to return rdev->broken_syshnd. */
rdev - > broken_syshnd = syshnd ; /* mysql_get_socket() over a failed mariadb handle ends up with segfault */
/* this attempts to trigger the low-level multiplxer to delete 'syshnd' closed by mysql_real_connect_cont().
* the underlying low - level operation may fail . but i don ' t care . the best is not to open
* new file descriptor between mysql_real_connect_cont ( ) and watch_mysql ( rdev , 0 ) .
*
* close ( 6 ) ; < - mysql_real_connect_cont ( ) ;
* epoll_ctl ( 4 , EPOLL_CTL_DEL , 6 , 0x7ffc785e7154 ) = - 1 EBADF ( Bad file descriptor ) < - by mio_dev_watch ( ) in watch_mysql
*/
watch_mysql ( rdev , 0 ) ;
/* on_disconnect() will be called without on_connect().
* you may assume that the initial connectinon attempt failed .
* reconnectin doesn ' t apply in this context . */
mio_dev_mar_halt ( rdev ) ;
}
2020-06-12 06:03:00 +00:00
}
}
break ;
2020-06-14 16:09:17 +00:00
case MIO_DEV_MAR_QUERY_STARTING :
2020-06-24 03:00:46 +00:00
if ( rdev - > query_started_deferred )
2020-06-12 06:03:00 +00:00
{
2020-06-24 03:00:46 +00:00
rdev - > query_started_deferred = 0 ;
2020-06-14 16:09:17 +00:00
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_QUERY_STARTED ) ;
2020-06-24 03:00:46 +00:00
if ( rdev - > on_query_started ) rdev - > on_query_started ( rdev , 0 , MIO_NULL ) ;
2020-06-12 06:03:00 +00:00
}
else
{
2020-06-24 03:00:46 +00:00
int status , err ;
mio_syshnd_t syshnd ;
2020-06-12 06:03:00 +00:00
2020-06-24 03:00:46 +00:00
syshnd = mysql_get_socket ( rdev - > hnd ) ;
status = mysql_real_query_cont ( & err , rdev - > hnd , events_to_mysql_wstatus ( events ) ) ;
2020-06-12 06:03:00 +00:00
2020-06-24 03:00:46 +00:00
if ( status )
{
watch_mysql ( rdev , status ) ;
}
else
2020-06-12 06:03:00 +00:00
{
2020-06-24 03:00:46 +00:00
if ( err )
{
/* query send failure */
if ( err = = 1 ) err = mysql_errno ( rdev - > hnd ) ; /* err 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 ( err = = CR_SERVER_LOST | | err = = CR_SERVER_GONE_ERROR )
{
rdev - > broken = 1 ;
rdev - > broken_syshnd = syshnd ;
watch_mysql ( rdev , 0 ) ;
mio_dev_mar_halt ( rdev ) ; /* i can't keep this device alive regardless of the caller's post-action */
/* don't invoke on_query_started(). in this case, on_disconnect() will be called later */
}
else
{
/* query not sent for other reasons. probably nothing to watch? */
watch_mysql ( rdev , 0 ) ; /* TODO: use status instead of 0? is status reliable in this context? */
if ( rdev - > on_query_started ) rdev - > on_query_started ( rdev , err , mysql_error ( rdev - > hnd ) ) ;
}
}
else
{
/* query really sent */
MIO_DEV_MAR_SET_PROGRESS ( rdev , MIO_DEV_MAR_QUERY_STARTED ) ;
if ( rdev - > on_query_started ) rdev - > on_query_started ( rdev , 0 , 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 ;
2020-06-24 07:37:11 +00:00
if ( rdev - > row_fetched_deferred )
2020-06-12 06:03:00 +00:00
{
row = ( MYSQL_ROW ) rdev - > row ;
2020-06-24 07:37:11 +00:00
rdev - > row_fetched_deferred = 0 ;
2020-06-12 06:03:00 +00:00
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-24 07:37:11 +00:00
if ( MIO_LIKELY ( rdev - > on_row_fetched ) ) rdev - > on_row_fetched ( rdev , row ) ;
2020-06-12 06:03:00 +00:00
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 )
{
2020-06-24 07:37:11 +00:00
/* row is available */
2020-06-12 06:03:00 +00:00
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-24 07:37:11 +00:00
if ( MIO_LIKELY ( rdev - > on_row_fetched ) ) rdev - > on_row_fetched ( rdev , row ) ;
2020-06-12 06:03:00 +00:00
if ( row ) start_fetch_row ( rdev ) ; /* arrange to fetch the next row */
}
else
{
2020-06-24 07:37:11 +00:00
/* no row is available */
2020-06-12 06:03:00 +00:00
watch_mysql ( rdev , status ) ;
}
}
break ;
}
default :
2020-06-24 03:00:46 +00:00
mio_seterrbfmt ( mio , MIO_EINTERN , " invalid progress value 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
2020-06-24 07:37:11 +00:00
mio_oow_t mio_dev_mar_escapebchars ( mio_dev_mar_t * dev , const mio_bch_t * qstr , mio_oow_t qlen , mio_bch_t * buf )
{
mio_dev_mar_t * rdev = ( mio_dev_mar_t * ) dev ;
return mysql_real_escape_string ( rdev - > hnd , buf , qstr , qlen ) ;
}