diff --git a/CMakeLists.txt b/CMakeLists.txt index d7a18ddd..7eb82b72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ # CMakeLists.txt - # This is the LGPL libmariadb project. PROJECT(mariadb-connector-c C) diff --git a/include/mariadb/ma_io.h b/include/mariadb/ma_io.h new file mode 100644 index 00000000..84d42d0a --- /dev/null +++ b/include/mariadb/ma_io.h @@ -0,0 +1,33 @@ +#ifndef _ma_io_h_ +#define _ma_io_h_ + +#include + +enum enum_file_type { + MA_FILE_NONE=0, + MA_FILE_LOCAL=1, + MA_FILE_REMOTE=2 +}; + +typedef struct +{ + enum enum_file_type type; + void *ptr; +} MA_FILE; + +struct st_rio_methods { + MA_FILE *(*open)(const char *url, const char *mode); + int (*close)(MA_FILE *ptr); + int (*feof)(MA_FILE *file); + size_t (*read)(void *ptr, size_t size, size_t nmemb, MA_FILE *file); + char * (*gets)(char *ptr, size_t size, MA_FILE *file); +}; + +/* function prototypes */ +MA_FILE *ma_open(const char *location, const char *mode, MYSQL *mysql); +int ma_close(MA_FILE *file); +int ma_feof(MA_FILE *file); +size_t ma_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file); +char *ma_gets(char *ptr, size_t size, MA_FILE *file); + +#endif diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index 1e706c2c..f0e04637 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -39,11 +39,14 @@ #define MYSQL_CLIENT_DB_PLUGIN 0 #define MYSQL_CLIENT_reserved 1 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 +#define MYSQL_CLIENT_reserved22 3 +#define MYSQL_CLIENT_REMOTEIO_PLUGIN 4 #define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100 #define MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION 0x0100 +#define MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION 0x0100 -#define MYSQL_CLIENT_MAX_PLUGINS 3 +#define MYSQL_CLIENT_MAX_PLUGINS 5 #define mysql_declare_client_plugin(X) \ struct st_mysql_client_plugin_ ## X \ @@ -76,22 +79,6 @@ typedef struct st_mariadb_client_plugin_DB MYSQL_CLIENT_PLUGIN_HEADER /* functions */ struct st_mysql_methods *methods; - /* - MYSQL * (*db_connect)(MYSQL *mysql,const char *host, const char *user, - const char *passwd, const char *db, uint port, - const char *unix_socket,unsigned long client_flag); - void (*db_close)(MYSQL *mysql); - int (*db_query)(MYSQL *mysql, const char *query, size_t query_len); - int (*db_read_one_row)(MYSQL *mysql, uint fields, MYSQL_ROW row, - ulong *lengths); - MYSQL_DATA *(*db_read_all_rows)(MYSQL *mysql, - MYSQL_FIELD *mysql_fields, uint fields); - void (*db_query_end)(MYSQL *mysql); - int (*db_stmt_prepare)(MYSQL_STMT *stmt, const char *stmt_str, ulong length); - my_bool (*db_stmt_close)(MYSQL_STMT *stmt); - my_bool (*is_supported_buffer_type)(enum enum_field_types type); - int (*db_stmt_fetch)(MYSQL_STMT *stmt); - int (*db_stmt_execute)(MYSQL_STMT *stmt); */ } MARIADB_DB_PLUGIN; #define MARIADB_DB_DRIVER(a) ((a)->ext_db) @@ -105,6 +92,7 @@ struct st_mysql_client_plugin_AUTHENTICATION int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql); }; + /** type of the mysql_authentication_dialog_ask function @@ -123,6 +111,17 @@ struct st_mysql_client_plugin_AUTHENTICATION */ typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql, int type, const char *prompt, char *buf, int buf_len); + +/********************** remote IO plugin **********************/ +#include + +/* Remote IO plugin */ +struct st_mysql_client_plugin_REMOTEIO +{ + MYSQL_CLIENT_PLUGIN_HEADER + struct st_rio_methods *methods; +}; + /******** using plugins ************/ /** diff --git a/libmariadb/CMakeLists.txt b/libmariadb/CMakeLists.txt index 1f07d901..3ea50db2 100644 --- a/libmariadb/CMakeLists.txt +++ b/libmariadb/CMakeLists.txt @@ -263,7 +263,8 @@ is_prefix.c libmariadb.c list.c llstr.c -longlong2str.c +longlong2str.c +ma_io.c mf_dirname.c mf_fn_ext.c mf_format.c diff --git a/libmariadb/client_plugin.c b/libmariadb/client_plugin.c index f2757f51..e8a5ee2b 100644 --- a/libmariadb/client_plugin.c +++ b/libmariadb/client_plugin.c @@ -62,7 +62,25 @@ static uint plugin_version[MYSQL_CLIENT_MAX_PLUGINS]= { MYSQL_CLIENT_DB_PLUGIN_INTERFACE_VERSION, /* these two are taken by Connector/C */ 0, /* these two are taken by Connector/C */ - MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + 0, + MYSQL_CLIENT_REMOTEIO_PLUGIN_INTERFACE_VERSION +}; + +typedef struct st_mysql_client_plugin_AUTHENTICATION auth_plugin_t; +extern auth_plugin_t old_password_client_plugin; +extern auth_plugin_t native_password_client_plugin; + +/* built in plugins: + These plugins are part of Connector/C, so no need to + load them +*/ + +struct st_mysql_client_plugin *mysql_client_builtins[]= +{ + (struct st_mysql_client_plugin *)&old_password_client_plugin, + (struct st_mysql_client_plugin *)&native_password_client_plugin, + 0 }; /* @@ -85,7 +103,7 @@ static int is_not_initialized(MYSQL *mysql, const char *name) my_set_error(mysql, CR_AUTH_PLUGIN_CANNOT_LOAD, SQLSTATE_UNKNOWN, ER(CR_AUTH_PLUGIN_CANNOT_LOAD), - name, "not initialized"); + name ? name : "unknown" , "not initialized"); return 1; } @@ -112,13 +130,14 @@ static struct st_mysql_client_plugin *find_plugin(const char *name, int type) for (p= plugin_list[type]; p; p= p->next) { + if (!name) + return p->plugin; if (strcmp(p->plugin->name, name) == 0) return p->plugin; } return NULL; } - /** verifies the plugin and adds it to the list @@ -266,6 +285,7 @@ int mysql_client_plugin_init() for (builtin= mysql_client_builtins; *builtin; builtin++) add_plugin(&mysql, *builtin, 0, 0, unused); + pthread_mutex_unlock(&LOCK_load_client_plugin); load_env_plugins(&mysql); @@ -456,7 +476,8 @@ mysql_client_find_plugin(MYSQL *mysql, const char *name, int type) return p; /* not found, load it */ - return mysql_load_plugin(mysql, name, type, 0); + if (name) + return mysql_load_plugin(mysql, name, type, 0); } diff --git a/libmariadb/default.c b/libmariadb/default.c index df762930..20fa4cf3 100644 --- a/libmariadb/default.c +++ b/libmariadb/default.c @@ -41,6 +41,8 @@ #include #include "m_ctype.h" #include +#include +#include char *defaults_extra_file=0; @@ -225,42 +227,49 @@ static my_bool search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, const char *ext, TYPELIB *group) { char name[FN_REFLEN+10],buff[4096],*ptr,*end,*value,*tmp; - FILE *fp; + MA_FILE *file; uint line=0; my_bool read_values= 0, found_group= 0, is_escaped= 0, is_quoted= 0; - if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3) - return 0; /* Ignore wrong paths */ - if (dir) + if (!strstr(config_file, "://")) { - strmov(name,dir); - convert_dirname(name); - if (dir[0] == FN_HOMELIB) /* Add . to filenames in home */ - strcat(name,"."); - strxmov(strend(name),config_file,ext,NullS); - } - else - { - strmov(name,config_file); - } - fn_format(name,name,"","",4); -#if !defined(_WIN32) && !defined(OS2) - { - MY_STAT stat_info; - if (!my_stat(name,&stat_info,MYF(0))) - return 0; - if (stat_info.st_mode & S_IWOTH) /* ignore world-writeable files */ + if ((dir ? strlen(dir) : 0 )+strlen(config_file) >= FN_REFLEN-3) + return 0; /* Ignore wrong paths */ + if (dir) { - fprintf(stderr, "warning: World-writeable config file %s is ignored\n", - name); - return 0; + strmov(name,dir); + convert_dirname(name); + if (dir[0] == FN_HOMELIB) /* Add . to filenames in home */ + strcat(name,"."); + strxmov(strend(name),config_file,ext,NullS); + } + else + { + strmov(name,config_file); + } + fn_format(name,name,"","",4); +#if !defined(_WIN32) && !defined(OS2) + { + MY_STAT stat_info; + if (!my_stat(name,&stat_info,MYF(0))) + return 0; + if (stat_info.st_mode & S_IWOTH) /* ignore world-writeable files */ + { + fprintf(stderr, "warning: World-writeable config file %s is ignored\n", + name); + return 0; + } } - } #endif - if (!(fp = my_fopen(fn_format(name,name,"","",4),O_RDONLY,MYF(0)))) - return 0; /* Ignore wrong files */ + if (!(file = ma_open(fn_format(name,name,"","",4),"r", NULL))) + return 0; + } + else { + if (!(file = ma_open(config_file, "r", NULL))) + return 0; + } - while (fgets(buff,sizeof(buff)-1,fp)) + while (ma_gets(buff,sizeof(buff)-1,file)) { line++; /* Ignore comment and empty lines */ @@ -372,11 +381,11 @@ static my_bool search_default_file(DYNAMIC_ARRAY *args, MEM_ROOT *alloc, *ptr=0; } } - my_fclose(fp,MYF(0)); + ma_close(file); return(0); err: - my_fclose(fp,MYF(0)); + ma_close(file); return 1; } diff --git a/libmariadb/libmariadb.c b/libmariadb/libmariadb.c index 1d8b2e80..b6f87d9a 100644 --- a/libmariadb/libmariadb.c +++ b/libmariadb/libmariadb.c @@ -32,6 +32,7 @@ #include "mysql.h" #include "mysql_version.h" #include "mysqld_error.h" +#include #include "errmsg.h" #include #include diff --git a/libmariadb/my_auth.c b/libmariadb/my_auth.c index e5ddfe20..71dc46f9 100644 --- a/libmariadb/my_auth.c +++ b/libmariadb/my_auth.c @@ -21,7 +21,7 @@ do {\ typedef char constraint[(A) ? 1 : -1];\ } while (0); -static auth_plugin_t native_password_client_plugin= +auth_plugin_t native_password_client_plugin= { MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, @@ -34,7 +34,7 @@ static auth_plugin_t native_password_client_plugin= native_password_auth_client }; -static auth_plugin_t old_password_client_plugin= +auth_plugin_t old_password_client_plugin= { MYSQL_CLIENT_AUTHENTICATION_PLUGIN, MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, @@ -46,21 +46,6 @@ static auth_plugin_t old_password_client_plugin= NULL, old_password_auth_client }; -typedef struct st_mariadb_client_plugin_DBAPI dbapi_plugin_t; - -#ifdef HAVE_SQLITE -extern dbapi_plugin_t sqlite3_plugin; -#endif - -struct st_mysql_client_plugin *mysql_client_builtins[]= -{ - (struct st_mysql_client_plugin *)&old_password_client_plugin, - (struct st_mysql_client_plugin *)&native_password_client_plugin, -#ifdef HAVE_SQLITE - (struct st_mysql_client_plugin *)&sqlite3_plugin, -#endif - 0 -}; typedef struct { int (*read_packet)(struct st_plugin_vio *vio, uchar **buf); diff --git a/libmariadb/my_loaddata.c b/libmariadb/my_loaddata.c index e01cfd08..f51098be 100644 --- a/libmariadb/my_loaddata.c +++ b/libmariadb/my_loaddata.c @@ -46,6 +46,7 @@ #include #include "errmsg.h" #include "mysql.h" +#include #include #ifdef _WIN32 #include @@ -53,7 +54,7 @@ typedef struct st_mysql_infile_info { - int fd; + MA_FILE *fp; int error_no; char error_msg[MYSQL_ERRMSG_SIZE + 1]; const char *filename; @@ -64,71 +65,37 @@ static int mysql_local_infile_init(void **ptr, const char *filename, void *userdata) { MYSQL_INFILE_INFO *info; - int CodePage= -1; -#ifdef _WIN32 MYSQL *mysql= (MYSQL *)userdata; - wchar_t *w_filename= NULL; - int Length; -#endif DBUG_ENTER("mysql_local_infile_init"); info = (MYSQL_INFILE_INFO *)my_malloc(sizeof(MYSQL_INFILE_INFO), MYF(MY_ZEROFILL)); if (!info) { DBUG_RETURN(1); } - *ptr = info; info->filename = filename; -#ifdef _WIN32 - if (mysql) - CodePage= madb_get_windows_cp(mysql->charset->csname); -#endif - if (CodePage == -1) - { -#ifdef _WIN32 - info->fd= sopen(info->filename, _O_RDONLY | _O_BINARY, _SH_DENYNO , _S_IREAD | _S_IWRITE); -#else - info->fd = open(info->filename, O_RDONLY | O_BINARY, my_umask); -#endif - my_errno= errno; - } -#ifdef _WIN32 - else - { - if ((Length= MultiByteToWideChar(CodePage, 0, info->filename, (int)strlen(info->filename), NULL, 0))) - { - if (!(w_filename= (wchar_t *)my_malloc((Length + 1) * sizeof(wchar_t), MYF(MY_ZEROFILL)))) - { - info->error_no= CR_OUT_OF_MEMORY; - my_snprintf((char *)info->error_msg, sizeof(info->error_msg), - ER(CR_OUT_OF_MEMORY)); - DBUG_RETURN(1); - } - Length= MultiByteToWideChar(CodePage, 0, info->filename, (int)strlen(info->filename), w_filename, (int)Length); - } - if (Length == 0) - { - my_free(w_filename); - info->error_no= CR_UNKNOWN_ERROR; - my_snprintf((char *)info->error_msg, sizeof(info->error_msg), - "Character conversion error: %d", GetLastError()); - DBUG_RETURN(1); - } - info->fd= _wsopen(w_filename, _O_RDONLY | _O_BINARY, _SH_DENYNO , _S_IREAD | _S_IWRITE); - my_errno= errno; - my_free(w_filename); - } -#endif + info->fp= ma_open(filename, "rb", mysql); - if (info->fd < 0) + if (!info->fp) { - info->error_no = my_errno; - my_snprintf((char *)info->error_msg, sizeof(info->error_msg), - EE(EE_FILENOTFOUND), filename, info->error_no); + /* error handling is done via mysql_local_infile_error function, so we + need to copy error to info */ + if (mysql_errno(mysql) && !info->error_no) + { + info->error_no= mysql_errno(mysql); + strncpy(info->error_msg, mysql_error(mysql), MYSQL_ERRMSG_SIZE); + } + else + { + info->error_no = my_errno; + my_snprintf((char *)info->error_msg, sizeof(info->error_msg), + EE(EE_FILENOTFOUND), filename, info->error_no); + } DBUG_RETURN(1); } + DBUG_RETURN(0); } /* }}} */ @@ -139,11 +106,11 @@ static int mysql_local_infile_read(void *ptr, char * buf, unsigned int buf_len) { MYSQL_INFILE_INFO *info = (MYSQL_INFILE_INFO *)ptr; - int count; + size_t count; DBUG_ENTER("mysql_local_infile_read"); - count= read(info->fd, (void *)buf, (size_t)buf_len); + count= ma_read((void *)buf, 1, (size_t)buf_len, info->fp); if (count < 0) { @@ -184,8 +151,8 @@ void mysql_local_infile_end(void *ptr) if (info) { - if (info->fd >= 0) - close(info->fd); + if (info->fp) + ma_close(info->fp); my_free(ptr); } DBUG_VOID_RETURN; diff --git a/plugins/io/CMakeLists.txt b/plugins/io/CMakeLists.txt new file mode 100644 index 00000000..aa615d58 --- /dev/null +++ b/plugins/io/CMakeLists.txt @@ -0,0 +1,19 @@ +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) + +FIND_PACKAGE(CURL) +IF(CURL_FOUND) + ADD_DEFINITIONS(-DHAVE_CURL=1) + # remote file plugin + INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) + SET(REMOTE_IO_SOURCES remote_io.c) + ADD_LIBRARY(remote_io SHARED ${REMOTE_IO_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) + TARGET_LINK_LIBRARIES(remote_io ${CURL_LIBRARIES}) + + SET_TARGET_PROPERTIES(remote_io PROPERTIES PREFIX "") + + INSTALL(TARGETS + remote_io + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") +ENDIF() diff --git a/plugins/io/remote_io.c b/plugins/io/remote_io.c new file mode 100644 index 00000000..7a01b0e7 --- /dev/null +++ b/plugins/io/remote_io.c @@ -0,0 +1,433 @@ +/************************************************************************************ + * Copyright (C) 2015 Monty Program AB + * Copyright (c) 2003 Simtec Electronics + * + * Re-implemented by Vincent Sanders with extensive + * reference to original curl example code + * + * Rewritten for MariaDB Connector/C by Georg Richter + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * 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. + *************************************************************************************/ + +/* + This is a plugin for remote file access via libcurl. + + The following URL types are supported: + + http:// + https:// + ftp:// + sftp:// + ldap:// + smb:// +*/ + +#ifdef HAVE_CURL +#include +#include +#include +#include +#include +#include + +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include + +/* Internal file structure */ + +MA_FILE *ma_rio_open(const char *url,const char *operation); +int ma_rio_close(MA_FILE *file); +int ma_rio_feof(MA_FILE *file); +size_t ma_rio_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file); +char * ma_rio_gets(char *ptr, size_t size, MA_FILE *file); + +int ma_rio_init(char *, size_t, int, va_list); +int ma_rio_deinit(void); + +struct st_rio_methods ma_rio_methods= { + ma_rio_open, + ma_rio_close, + ma_rio_feof, + ma_rio_read, + ma_rio_gets +}; + +typedef struct +{ + CURL *curl; + size_t length, + offset; + uchar *buffer; + int in_progress; +} MA_REMOTE_FILE; + +CURLM *multi_handle= NULL; + +mysql_declare_client_plugin(REMOTEIO) + "remote_io", + "Georg Richter", + "Remote IO plugin", + {0,1,0}, + ma_rio_init, + ma_rio_deinit, + &ma_rio_methods +mysql_end_client_plugin; + +/* {{{ ma_rio_init - Plugin initialization */ +int ma_rio_init(char *unused1, size_t unused2, int unused3, va_list unused4) +{ + curl_global_init(CURL_GLOBAL_ALL); + if (!multi_handle) + multi_handle = curl_multi_init(); + return 0; +} +/* }}} */ + +/* {{{ ma_rio_deinit - Plugin deinitialization */ +int ma_rio_deinit(void) +{ + if (multi_handle) + { + curl_multi_cleanup(multi_handle); + multi_handle= NULL; + } + curl_global_cleanup(); +} +/* }}} */ + +/* {{{ curl_write_callback */ +static size_t rio_write_callback(char *buffer, + size_t size, + size_t nitems, + void *ptr) +{ + size_t free_bytes; + char *tmp; + + MA_FILE *file= (MA_FILE *)ptr; + MA_REMOTE_FILE *curl_file = (MA_REMOTE_FILE *)file->ptr; + size *= nitems; + + free_bytes= curl_file->length - curl_file->offset; + + /* check if we need to allocate more memory */ + if (size > free_bytes) { + tmp= (char *)realloc((gptr)curl_file->buffer, curl_file->length + (size - free_bytes)); + if (!tmp) + size= free_bytes; + else { + curl_file->length+= size - free_bytes; + curl_file->buffer= tmp; + } + } + + /* copy buffer into MA_FILE structure */ + memcpy((char *)curl_file->buffer + curl_file->offset, buffer, size); + curl_file->offset+= size; + + return size; +} +/* }}} */ + +/* use to attempt to fill the read buffer up to requested number of bytes */ +static int fill_buffer(MA_FILE *file, size_t want) +{ + fd_set fdread; + fd_set fdwrite; + fd_set fdexcep; + struct timeval timeout; + int rc; + CURLMcode mc; /* curl_multi_fdset() return code */ + MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr; + + /* only attempt to fill buffer if transactions still running and buffer + doesnt exceed required size already */ + if (!rf->in_progress || (rf->offset > want)) + return 0; + + /* try to fill buffer */ + do { + int maxfd = -1; + long curl_timeo = -1; + + FD_ZERO(&fdread); + FD_ZERO(&fdwrite); + FD_ZERO(&fdexcep); + + /* set a suitable timeout to fail on */ + timeout.tv_sec = 20; /* 20 seconds */ + timeout.tv_usec = 0; + + curl_multi_timeout(multi_handle, &curl_timeo); + if(curl_timeo >= 0) { + timeout.tv_sec = curl_timeo / 1000; + if(timeout.tv_sec > 1) + timeout.tv_sec = 1; + else + timeout.tv_usec = (curl_timeo % 1000) * 1000; + } + + /* get file descriptors from the transfers */ + mc = curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd); + + if(mc != CURLM_OK) + { + /* todo: error handling */ + break; + } + + /* On success the value of maxfd is guaranteed to be >= -1. We call + select(maxfd + 1, ...); specially in case of (maxfd == -1) there are + no fds ready yet so we call select(0, ...) */ + + if(maxfd == -1) { + struct timeval wait = { 0, 100 * 1000 }; /* 100ms */ + rc = select(0, NULL, NULL, NULL, &wait); + } + else { + rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout); + } + + switch(rc) { + case -1: + /* select error */ + break; + + case 0: + default: + /* timeout or readable/writable sockets */ + curl_multi_perform(multi_handle, &rf->in_progress); + break; + } + } while(rf->in_progress && (rf->offset < want)); + return 1; +} + +/* use to remove want bytes from the front of a files buffer */ +static int use_buffer(MA_FILE *file,int want) +{ + MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr; + /* sort out buffer */ + if((rf->offset - want) <=0) { + /* ditch buffer - write will recreate */ + if (rf->buffer) + free(rf->buffer); + + rf->buffer=NULL; + rf->offset=0; + rf->length=0; + } + else { + /* move rest down make it available for later */ + memmove(rf->buffer, + &rf->buffer[want], + (rf->offset - want)); + + rf->offset -= want; + } + return 0; +} + +MA_FILE *ma_rio_open(const char *url,const char *operation) +{ + /* this code could check for URLs or types in the 'url' and + basicly use the real fopen() for standard files */ + + MA_FILE *file; + MA_REMOTE_FILE *rf; + (void)operation; + + if (!(file = (MA_FILE *)calloc(sizeof(MA_FILE), 1))) + return NULL; + + file->type= MA_FILE_REMOTE; + if (!(file->ptr= rf= (MA_REMOTE_FILE *)calloc(sizeof(MA_REMOTE_FILE), 1))) + { + free(file); + return NULL; + } + rf->curl = curl_easy_init(); + + curl_easy_setopt(rf->curl, CURLOPT_URL, url); + curl_easy_setopt(rf->curl, CURLOPT_WRITEDATA, file); + curl_easy_setopt(rf->curl, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(rf->curl, CURLOPT_WRITEFUNCTION, rio_write_callback); + + curl_multi_add_handle(multi_handle, rf->curl); + + /* lets start the fetch */ + curl_multi_perform(multi_handle, &rf->in_progress); + + if((rf->offset == 0) && (!rf->in_progress)) { + /* if in_progress is 0 now, we should return NULL */ + + /* make sure the easy handle is not in the multi handle anymore */ + curl_multi_remove_handle(multi_handle, rf->curl); + + /* cleanup */ + curl_easy_cleanup(rf->curl); + + free(file); + + file = NULL; + } + return file; +} + +int ma_rio_close(MA_FILE *file) +{ + int ret=0;/* default is good return */ + MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr; + + switch(file->type) { + case MA_FILE_REMOTE: + curl_multi_remove_handle(multi_handle, rf->curl); + + /* cleanup */ + curl_easy_cleanup(rf->curl); + break; + + default: /* unknown or supported type - oh dear */ + ret=EOF; + errno=EBADF; + break; + } + + if(rf->buffer) + free(rf->buffer);/* free any allocated buffer space */ + + free(rf); + free(file); + + return ret; +} + +int ma_rio_feof(MA_FILE *file) +{ + int ret=0; + MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr; + + switch(file->type) { + case MA_FILE_REMOTE: + if((rf->offset == 0) && (!rf->in_progress)) + ret = 1; + break; + + default: /* unknown or supported type - oh dear */ + ret=-1; + errno=EBADF; + break; + } + return ret; +} + +size_t ma_rio_read(void *ptr, size_t size, size_t nmemb, MA_FILE *file) +{ + size_t want; + MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr; + + switch(file->type) { + case MA_FILE_REMOTE: + want = nmemb * size; + + fill_buffer(file,want); + + /* check if theres data in the buffer - if not fill_buffer() + * either errored or EOF */ + if(!rf->offset) + return 0; + + /* ensure only available data is considered */ + if(rf->offset < want) + want = rf->offset; + + /* xfer data to caller */ + memcpy(ptr, rf->buffer, want); + + use_buffer(file,want); + + want = want / size; /* number of items */ + break; + + default: /* unknown or supported type - oh dear */ + want=0; + errno=EBADF; + break; + + } + return want; +} + +char *ma_rio_gets(char *ptr, size_t size, MA_FILE *file) +{ + size_t want = size - 1;/* always need to leave room for zero termination */ + size_t loop; + + switch(file->type) { + case MA_FILE_REMOTE: + { + MA_REMOTE_FILE *rf= (MA_REMOTE_FILE *)file->ptr; + fill_buffer(file,want); + + /* check if theres data in the buffer - if not fill either errored or + * EOF */ + if(!rf->offset) + return NULL; + + /* ensure only available data is considered */ + if(rf->offset < want) + want = rf->offset; + + /*buffer contains data */ + /* look for newline or eof */ + for(loop=0;loop < want;loop++) { + if(rf->buffer[loop] == '\n') { + want=loop+1;/* include newline */ + break; + } + } + + /* xfer data to caller */ + memcpy(ptr, rf->buffer, want); + ptr[want]=0;/* allways null terminate */ + + use_buffer(file,want); + + break; + } + + default: /* unknown or supported type - oh dear */ + ptr=NULL; + errno=EBADF; + break; + } + + return ptr;/*success */ +} +#endif diff --git a/unittest/libmariadb/misc.c b/unittest/libmariadb/misc.c index b21ff10e..a91b79cd 100644 --- a/unittest/libmariadb/misc.c +++ b/unittest/libmariadb/misc.c @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include +void *remote_plugin; /* Bug#28075 "COM_DEBUG crashes mysqld" @@ -917,7 +918,7 @@ static int test_connect_attrs(MYSQL *my) rc= mysql_query(my, "SELECT * FROM performance_schema.session_connect_attrs LIMIT 1"); if (rc != 0) { - diag("Server doesn't connection attributes"); + diag("Server doesn't support connection attributes"); return SKIP; } @@ -1001,7 +1002,55 @@ static int test_conc117(MYSQL *mysql) return OK; } +static int test_remote1(MYSQL *mysql) +{ + int rc; + + remote_plugin= (void *)mysql_client_find_plugin(mysql, "remote_io", MYSQL_CLIENT_REMOTEIO_PLUGIN); + if (!remote_plugin) + { + diag("skip - no remote io plugin available"); + return SKIP; + } + + rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "CREATE TABLE t1 (a text)"); + check_mysql_rc(rc, mysql); + + rc= mysql_query(mysql, "LOAD DATA LOCAL INFILE 'http://www.example.com' INTO TABLE t1"); + if (rc && mysql_errno(mysql) == 2058) + { + diag("remote_io plugin not available"); + return SKIP; + } + check_mysql_rc(rc, mysql); + return OK; +} + +static int test_remote2(MYSQL *my) +{ + MYSQL *mysql= mysql_init(NULL); + + if (!remote_plugin) + { + diag("skip - no remote io plugin available"); + return SKIP; + } + + mysql_options(mysql, MYSQL_READ_DEFAULT_FILE, "http://localhost/test.cnf"); + mysql_options(mysql, MYSQL_READ_DEFAULT_GROUP, "test"); + mysql_real_connect(mysql, hostname, username, password, schema, + 0, socketname, 0), mysql_error(my); + diag("port: %d", mysql->port); + mysql_close(mysql); + return OK; +} + struct my_tests_st my_tests[] = { + {"test_remote1", test_remote1, TEST_CONNECTION_NEW, 0, NULL, NULL}, + {"test_remote2", test_remote2, TEST_CONNECTION_NEW, 0, NULL, NULL}, {"test_conc117", test_conc117, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_conc_114", test_conc_114, TEST_CONNECTION_DEFAULT, 0, NULL, NULL}, {"test_connect_attrs", test_connect_attrs, TEST_CONNECTION_DEFAULT, 0, NULL, NULL},