diff options
Diffstat (limited to 'sql-common')
-rw-r--r-- | sql-common/Makefile.am | 2 | ||||
-rw-r--r-- | sql-common/client.c | 413 | ||||
-rw-r--r-- | sql-common/my_time.c | 259 | ||||
-rw-r--r-- | sql-common/my_user.c | 57 |
4 files changed, 649 insertions, 82 deletions
diff --git a/sql-common/Makefile.am b/sql-common/Makefile.am index 6bd42d70e4f..d71523a741c 100644 --- a/sql-common/Makefile.am +++ b/sql-common/Makefile.am @@ -15,7 +15,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## Process this file with automake to create Makefile.in -EXTRA_DIST = client.c pack.c my_time.c +EXTRA_DIST = client.c pack.c my_time.c my_user.c # Don't update the files from bitkeeper %::SCCS/s.% diff --git a/sql-common/client.c b/sql-common/client.c index 4d37b850bcb..fc483e7b4c7 100644 --- a/sql-common/client.c +++ b/sql-common/client.c @@ -57,7 +57,7 @@ my_bool net_flush(NET *net); #else /*EMBEDDED_LIBRARY*/ -#define CLI_MYSQL_REAL_CONNECT mysql_real_connect +#define CLI_MYSQL_REAL_CONNECT STDCALL mysql_real_connect #endif /*EMBEDDED_LIBRARY*/ #include <my_sys.h> #include <mysys_err.h> @@ -98,9 +98,6 @@ my_bool net_flush(NET *net); # include <sys/un.h> #endif -#ifndef INADDR_NONE -#define INADDR_NONE -1 -#endif #if defined(MSDOS) || defined(__WIN__) #define perror(A) #else @@ -587,7 +584,7 @@ err: *****************************************************************************/ ulong -net_safe_read(MYSQL *mysql) +cli_safe_read(MYSQL *mysql) { NET *net= &mysql->net; ulong len=0; @@ -604,7 +601,7 @@ net_safe_read(MYSQL *mysql) DBUG_PRINT("error",("Wrong connection or packet. fd: %s len: %d", vio_description(net->vio),len)); #ifdef MYSQL_SERVER - if (vio_was_interrupted(net->vio)) + if (net->vio && vio_was_interrupted(net->vio)) return (packet_error); #endif /*MYSQL_SERVER*/ end_server(mysql); @@ -630,6 +627,16 @@ net_safe_read(MYSQL *mysql) } else set_mysql_error(mysql, CR_UNKNOWN_ERROR, unknown_sqlstate); + /* + Cover a protocol design error: error packet does not + contain the server status. Therefore, the client has no way + to find out whether there are more result sets of + a multiple-result-set statement pending. Luckily, in 5.0 an + error always aborts execution of a statement, wherever it is + a multi-statement or a stored procedure, so it should be + safe to unconditionally turn off the flag here. + */ + mysql->server_status&= ~SERVER_MORE_RESULTS_EXISTS; DBUG_PRINT("error",("Got error: %d/%s (%s)", net->last_errno, net->sqlstate, net->last_error)); @@ -656,6 +663,7 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, NET *net= &mysql->net; my_bool result= 1; init_sigpipe_variables + DBUG_ENTER("cli_advanced_command"); /* Don't give sigpipe errors if the client doesn't want them */ set_sigpipe(mysql); @@ -663,12 +671,14 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, if (mysql->net.vio == 0) { /* Do reconnect if possible */ if (mysql_reconnect(mysql)) - return 1; + DBUG_RETURN(1); } - if (mysql->status != MYSQL_STATUS_READY) + if (mysql->status != MYSQL_STATUS_READY || + mysql->server_status & SERVER_MORE_RESULTS_EXISTS) { + DBUG_PRINT("error",("state: %d", mysql->status)); set_mysql_error(mysql, CR_COMMANDS_OUT_OF_SYNC, unknown_sqlstate); - return 1; + DBUG_RETURN(1); } net->last_error[0]=0; @@ -703,11 +713,12 @@ cli_advanced_command(MYSQL *mysql, enum enum_server_command command, } result=0; if (!skip_check) - result= ((mysql->packet_length=net_safe_read(mysql)) == packet_error ? + result= ((mysql->packet_length=cli_safe_read(mysql)) == packet_error ? 1 : 0); end: reset_sigpipe(mysql); - return result; + DBUG_PRINT("exit",("result: %d", result)); + DBUG_RETURN(result); } void free_old_query(MYSQL *mysql) @@ -718,6 +729,7 @@ void free_old_query(MYSQL *mysql) init_alloc_root(&mysql->field_alloc,8192,0); /* Assume rowlength < 8192 */ mysql->fields= 0; mysql->field_count= 0; /* For API */ + mysql->warning_count= 0; mysql->info= 0; DBUG_VOID_RETURN; } @@ -753,7 +765,7 @@ static void cli_flush_use_result(MYSQL *mysql) for (;;) { ulong pkt_len; - if ((pkt_len=net_safe_read(mysql)) == packet_error) + if ((pkt_len=cli_safe_read(mysql)) == packet_error) break; if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) { @@ -870,6 +882,8 @@ mysql_free_result(MYSQL_RES *result) { (*mysql->methods->flush_use_result)(mysql); mysql->status=MYSQL_STATUS_READY; + if (mysql->unbuffered_fetch_owner) + *mysql->unbuffered_fetch_owner= TRUE; } } free_rows(result->data); @@ -896,6 +910,7 @@ static const char *default_options[]= "replication-probe", "enable-reads-from-master", "repl-parse-query", "ssl-cipher", "max-allowed-packet", "protocol", "shared-memory-base-name", "multi-results", "multi-statements", "multi-queries", "secure-auth", + "report-data-truncation", NullS }; @@ -1107,6 +1122,9 @@ void mysql_read_default_options(struct st_mysql_options *options, case 33: /* secure-auth */ options->secure_auth= TRUE; break; + case 34: /* report-data-truncation */ + options->report_data_truncation= opt_arg ? test(atoi(opt_arg)) : 1; + break; default: DBUG_PRINT("warning",("unknown option: %s",option[0])); } @@ -1269,7 +1287,7 @@ MYSQL_DATA *cli_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, NET *net = &mysql->net; DBUG_ENTER("cli_read_rows"); - if ((pkt_len= net_safe_read(mysql)) == packet_error) + if ((pkt_len= cli_safe_read(mysql)) == packet_error) DBUG_RETURN(0); if (!(result=(MYSQL_DATA*) my_malloc(sizeof(MYSQL_DATA), MYF(MY_WME | MY_ZEROFILL)))) @@ -1334,7 +1352,7 @@ MYSQL_DATA *cli_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields, } } cur->data[field]=to; /* End of last field */ - if ((pkt_len=net_safe_read(mysql)) == packet_error) + if ((pkt_len=cli_safe_read(mysql)) == packet_error) { free_rows(result); DBUG_RETURN(0); @@ -1366,7 +1384,7 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths) uchar *pos, *prev_pos, *end_pos; NET *net= &mysql->net; - if ((pkt_len=net_safe_read(mysql)) == packet_error) + if ((pkt_len=cli_safe_read(mysql)) == packet_error) return -1; if (pkt_len <= 8 && net->read_pos[0] == 254) { @@ -1424,7 +1442,7 @@ mysql_init(MYSQL *mysql) mysql->free_me=1; } else - bzero((char*) (mysql),sizeof(*(mysql))); + bzero((char*) (mysql), sizeof(*(mysql))); mysql->options.connect_timeout= CONNECT_TIMEOUT; mysql->last_used_con= mysql->next_slave= mysql->master = mysql; mysql->charset=default_client_charset_info; @@ -1451,6 +1469,25 @@ mysql_init(MYSQL *mysql) #endif mysql->options.methods_to_use= MYSQL_OPT_GUESS_CONNECTION; + mysql->options.report_data_truncation= TRUE; /* default */ + + /* + By default we don't reconnect because it could silently corrupt data (after + reconnection you potentially lose table locks, user variables, session + variables (transactions but they are specifically dealt with in + mysql_reconnect()). + This is a change: < 5.0.3 mysql->reconnect was set to 1 by default. + How this change impacts existing apps: + - existing apps which relyed on the default will see a behaviour change; + they will have to set reconnect=1 after mysql_real_connect(). + - existing apps which explicitely asked for reconnection (the only way they + could do it was by setting mysql.reconnect to 1 after mysql_real_connect()) + will not see a behaviour change. + - existing apps which explicitely asked for no reconnection + (mysql.reconnect=0) will not see a behaviour change. + */ + mysql->reconnect= 0; + return mysql; } @@ -1470,6 +1507,7 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , const char *capath __attribute__((unused)), const char *cipher __attribute__((unused))) { + DBUG_ENTER("mysql_ssl_set"); #ifdef HAVE_OPENSSL mysql->options.ssl_key= strdup_if_not_null(key); mysql->options.ssl_cert= strdup_if_not_null(cert); @@ -1477,7 +1515,7 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , mysql->options.ssl_capath= strdup_if_not_null(capath); mysql->options.ssl_cipher= strdup_if_not_null(cipher); #endif /* HAVE_OPENSSL */ - return 0; + DBUG_RETURN(0); } @@ -1487,18 +1525,20 @@ mysql_ssl_set(MYSQL *mysql __attribute__((unused)) , */ #ifdef HAVE_OPENSSL + static void mysql_ssl_free(MYSQL *mysql __attribute__((unused))) { - struct st_VioSSLConnectorFd *st= - (struct st_VioSSLConnectorFd*) mysql->connector_fd; + struct st_VioSSLFd *ssl_fd= (struct st_VioSSLFd*) mysql->connector_fd; + DBUG_ENTER("mysql_ssl_free"); + my_free(mysql->options.ssl_key, MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.ssl_cert, MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.ssl_ca, MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.ssl_capath, MYF(MY_ALLOW_ZERO_PTR)); - my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); - if (st) - SSL_CTX_free(st->ssl_context); + my_free(mysql->options.ssl_cipher, MYF(MY_ALLOW_ZERO_PTR)); + if (ssl_fd) + SSL_CTX_free(ssl_fd->ssl_context); my_free(mysql->connector_fd,MYF(MY_ALLOW_ZERO_PTR)); mysql->options.ssl_key = 0; mysql->options.ssl_cert = 0; @@ -1507,7 +1547,106 @@ mysql_ssl_free(MYSQL *mysql __attribute__((unused))) mysql->options.ssl_cipher= 0; mysql->options.use_ssl = FALSE; mysql->connector_fd = 0; + DBUG_VOID_RETURN; } + +#endif /* HAVE_OPENSSL */ + +/* + Return the SSL cipher (if any) used for current + connection to the server. + + SYNOPSYS + mysql_get_ssl_cipher() + mysql pointer to the mysql connection + +*/ + +const char * STDCALL +mysql_get_ssl_cipher(MYSQL *mysql) +{ + DBUG_ENTER("mysql_get_ssl_cipher"); +#ifdef HAVE_OPENSSL + if (mysql->net.vio && mysql->net.vio->ssl_arg) + DBUG_RETURN(SSL_get_cipher_name((SSL*)mysql->net.vio->ssl_arg)); +#endif /* HAVE_OPENSSL */ + DBUG_RETURN(NULL); +} + + +/* + Check the server's (subject) Common Name against the + hostname we connected to + + SYNOPSIS + ssl_verify_server_cert() + vio pointer to a SSL connected vio + server_hostname name of the server that we connected to + + RETURN VALUES + 0 Success + 1 Failed to validate server + + */ + +#ifdef HAVE_OPENSSL + +static int ssl_verify_server_cert(Vio *vio, const char* server_hostname) +{ + SSL *ssl; + X509 *server_cert; + char *cp1, *cp2; + char buf[256]; + DBUG_ENTER("ssl_verify_server_cert"); + DBUG_PRINT("enter", ("server_hostname: %s", server_hostname)); + + if (!(ssl= (SSL*)vio->ssl_arg)) + { + DBUG_PRINT("error", ("No SSL pointer found")); + DBUG_RETURN(1); + } + + if (!server_hostname) + { + DBUG_PRINT("error", ("No server hostname supplied")); + DBUG_RETURN(1); + } + + if (!(server_cert= SSL_get_peer_certificate(ssl))) + { + DBUG_PRINT("error", ("Could not get server certificate")); + DBUG_RETURN(1); + } + + /* + We already know that the certificate exchanged was valid; the SSL library + handled that. Now we need to verify that the contents of the certificate + are what we expect. + */ + + X509_NAME_oneline(X509_get_subject_name(server_cert), buf, sizeof(buf)); + X509_free (server_cert); + + DBUG_PRINT("info", ("hostname in cert: %s", buf)); + cp1= strstr(buf, "/CN="); + if (cp1) + { + cp1+= 4; /* Skip the "/CN=" that we found */ + /* Search for next / which might be the delimiter for email */ + cp2= strchr(cp1, '/'); + if (cp2) + *cp2= '\0'; + DBUG_PRINT("info", ("Server hostname in cert: %s", cp1)); + if (!strcmp(cp1, server_hostname)) + { + /* Success */ + DBUG_RETURN(0); + } + } + DBUG_PRINT("error", ("SSL certificate validation failure")); + DBUG_RETURN(1); +} + #endif /* HAVE_OPENSSL */ @@ -1521,22 +1660,23 @@ static MYSQL_RES *cli_use_result(MYSQL *mysql); static MYSQL_METHODS client_methods= { - cli_read_query_result, - cli_advanced_command, - cli_read_rows, - cli_use_result, - cli_fetch_lengths, - cli_flush_use_result + cli_read_query_result, /* read_query_result */ + cli_advanced_command, /* advanced_command */ + cli_read_rows, /* read_rows */ + cli_use_result, /* use_result */ + cli_fetch_lengths, /* fetch_lengths */ + cli_flush_use_result /* flush_use_result */ #ifndef MYSQL_SERVER - ,cli_list_fields, - cli_read_prepare_result, - cli_stmt_execute, - cli_read_binary_rows, - cli_unbuffered_fetch, - NULL, - cli_read_statistics, - cli_read_query_result, - cli_read_change_user_result + ,cli_list_fields, /* list_fields */ + cli_read_prepare_result, /* read_prepare_result */ + cli_stmt_execute, /* stmt_execute */ + cli_read_binary_rows, /* read_binary_rows */ + cli_unbuffered_fetch, /* unbuffered_fetch */ + NULL, /* free_embedded_thd */ + cli_read_statistics, /* read_statistics */ + cli_read_query_result, /* next_result */ + cli_read_change_user_result, /* read_change_user_result */ + cli_read_binary_rows /* read_rows_from_cursor */ #endif }; @@ -1635,7 +1775,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, #ifdef HAVE_SYS_UN_H struct sockaddr_un UNIXaddr; #endif - init_sigpipe_variables DBUG_ENTER("mysql_real_connect"); LINT_INIT(host_info); @@ -1689,7 +1828,6 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, if (!unix_socket) unix_socket=mysql->options.unix_socket; - mysql->reconnect=1; /* Reconnect as default */ mysql->server_status=SERVER_STATUS_AUTOCOMMIT; /* @@ -1744,7 +1882,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ER(net->last_errno),socket_errno); goto error; } - net->vio = vio_new(sock, VIO_TYPE_SOCKET, TRUE); + net->vio= vio_new(sock, VIO_TYPE_SOCKET, + VIO_LOCALHOST | VIO_BUFFERED_READ); bzero((char*) &UNIXaddr,sizeof(UNIXaddr)); UNIXaddr.sun_family = AF_UNIX; strmake(UNIXaddr.sun_path, unix_socket, sizeof(UNIXaddr.sun_path)-1); @@ -1819,7 +1958,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, ER(net->last_errno),socket_errno); goto error; } - net->vio = vio_new(sock,VIO_TYPE_TCPIP,FALSE); + net->vio= vio_new(sock, VIO_TYPE_TCPIP, VIO_BUFFERED_READ); bzero((char*) &sock_addr,sizeof(sock_addr)); sock_addr.sin_family = AF_INET; @@ -1900,7 +2039,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, Part 1: Connection established, read and parse first packet */ - if ((pkt_length=net_safe_read(mysql)) == packet_error) + if ((pkt_length=cli_safe_read(mysql)) == packet_error) goto error; /* Check if version of protocol matches current one */ @@ -2024,37 +2163,52 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, mysql->client_flag=client_flag; #ifdef HAVE_OPENSSL - /* - Oops.. are we careful enough to not send ANY information without - encryption? - */ if (client_flag & CLIENT_SSL) { + /* Do the SSL layering. */ struct st_mysql_options *options= &mysql->options; + struct st_VioSSLFd *ssl_fd; + + /* + Send client_flag, max_packet_size - unencrypted otherwise + the server does not know we want to do SSL + */ if (my_net_write(net,buff,(uint) (end-buff)) || net_flush(net)) { set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); goto error; } - /* Do the SSL layering. */ - if (!(mysql->connector_fd= - (gptr) new_VioSSLConnectorFd(options->ssl_key, - options->ssl_cert, - options->ssl_ca, - options->ssl_capath, - options->ssl_cipher))) + + /* Create the VioSSLConnectorFd - init SSL and load certs */ + if (!(ssl_fd= new_VioSSLConnectorFd(options->ssl_key, + options->ssl_cert, + options->ssl_ca, + options->ssl_capath, + options->ssl_cipher))) { set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); goto error; } + mysql->connector_fd= (void*)ssl_fd; + + /* Connect to the server */ DBUG_PRINT("info", ("IO layer change in progress...")); - if (sslconnect((struct st_VioSSLConnectorFd*)(mysql->connector_fd), - mysql->net.vio, (long) (mysql->options.connect_timeout))) + if (sslconnect(ssl_fd, mysql->net.vio, + (long) (mysql->options.connect_timeout))) { set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); goto error; } DBUG_PRINT("info", ("IO layer change done!")); + + /* Verify server cert */ + if ((client_flag & CLIENT_SSL_VERIFY_SERVER_CERT) && + ssl_verify_server_cert(mysql->net.vio, mysql->host)) + { + set_mysql_error(mysql, CR_SSL_CONNECTION_ERROR, unknown_sqlstate); + goto error; + } + } #endif /* HAVE_OPENSSL */ @@ -2109,7 +2263,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, OK-packet, or re-request scrambled password. */ - if ((pkt_length=net_safe_read(mysql)) == packet_error) + if ((pkt_length=cli_safe_read(mysql)) == packet_error) goto error; if (pkt_length == 1 && net->read_pos[0] == 254 && @@ -2126,7 +2280,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, goto error; } /* Read what server thinks about out new auth message report */ - if (net_safe_read(mysql) == packet_error) + if (cli_safe_read(mysql) == packet_error) goto error; } @@ -2153,7 +2307,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user, for (; ptr<end; ptr++) { MYSQL_RES *res; - if (mysql_real_query(mysql,*ptr, strlen(*ptr))) + if (mysql_real_query(mysql,*ptr, (ulong) strlen(*ptr))) goto error; if (mysql->fields) { @@ -2225,8 +2379,9 @@ my_bool mysql_reconnect(MYSQL *mysql) DBUG_RETURN(1); } mysql_init(&tmp_mysql); - tmp_mysql.options=mysql->options; - tmp_mysql.rpl_pivot = mysql->rpl_pivot; + tmp_mysql.options= mysql->options; + tmp_mysql.rpl_pivot= mysql->rpl_pivot; + if (!mysql_real_connect(&tmp_mysql,mysql->host,mysql->user,mysql->passwd, mysql->db, mysql->port, mysql->unix_socket, mysql->client_flag | CLIENT_REMEMBER_OPTIONS)) @@ -2236,6 +2391,19 @@ my_bool mysql_reconnect(MYSQL *mysql) strmov(mysql->net.sqlstate, tmp_mysql.net.sqlstate); DBUG_RETURN(1); } + if (mysql_set_character_set(&tmp_mysql, mysql->charset->csname)) + { + DBUG_PRINT("error", ("mysql_set_character_set() failed")); + bzero((char*) &tmp_mysql.options,sizeof(tmp_mysql.options)); + mysql_close(&tmp_mysql); + mysql->net.last_errno= tmp_mysql.net.last_errno; + strmov(mysql->net.last_error, tmp_mysql.net.last_error); + strmov(mysql->net.sqlstate, tmp_mysql.net.sqlstate); + DBUG_RETURN(1); + } + + DBUG_PRINT("info", ("reconnect succeded")); + tmp_mysql.reconnect= 1; tmp_mysql.free_me= mysql->free_me; /* @@ -2298,6 +2466,8 @@ mysql_select_db(MYSQL *mysql, const char *db) static void mysql_close_free_options(MYSQL *mysql) { + DBUG_ENTER("mysql_close_free_options"); + my_free(mysql->options.user,MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.host,MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->options.password,MYF(MY_ALLOW_ZERO_PTR)); @@ -2326,6 +2496,7 @@ static void mysql_close_free_options(MYSQL *mysql) my_free(mysql->options.shared_memory_base_name,MYF(MY_ALLOW_ZERO_PTR)); #endif /* HAVE_SMEM */ bzero((char*) &mysql->options,sizeof(mysql->options)); + DBUG_VOID_RETURN; } @@ -2335,8 +2506,12 @@ static void mysql_close_free(MYSQL *mysql) my_free(mysql->user,MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->passwd,MYF(MY_ALLOW_ZERO_PTR)); my_free(mysql->db,MYF(MY_ALLOW_ZERO_PTR)); +#if defined(EMBEDDED_LIBRARY) || MYSQL_VERSION_ID >= 50100 + my_free(mysql->info_buffer,MYF(MY_ALLOW_ZERO_PTR)); + mysql->info_buffer= 0; +#endif /* Clear pointers for better safety */ - mysql->host_info=mysql->user=mysql->passwd=mysql->db=0; + mysql->host_info= mysql->user= mysql->passwd= mysql->db= 0; } @@ -2429,7 +2604,7 @@ static my_bool cli_read_query_result(MYSQL *mysql) */ mysql = mysql->last_used_con; - if ((length = net_safe_read(mysql)) == packet_error) + if ((length = cli_safe_read(mysql)) == packet_error) DBUG_RETURN(1); free_old_query(mysql); /* Free old result */ #ifdef MYSQL_CLIENT /* Avoid warn of unused labels*/ @@ -2464,7 +2639,7 @@ get_info: if (field_count == NULL_LENGTH) /* LOAD DATA LOCAL INFILE */ { int error=handle_local_infile(mysql,(char*) pos); - if ((length=net_safe_read(mysql)) == packet_error || error) + if ((length= cli_safe_read(mysql)) == packet_error || error) DBUG_RETURN(1); goto get_info; /* Get info packet */ } @@ -2472,10 +2647,7 @@ get_info: if (!(mysql->server_status & SERVER_STATUS_AUTOCOMMIT)) mysql->server_status|= SERVER_STATUS_IN_TRANS; - mysql->extra_info= net_field_length_ll(&pos); /* Maybe number of rec */ - - if (!(fields=(*mysql->methods->read_rows)(mysql,(MYSQL_FIELD*)0, - protocol_41(mysql) ? 7 : 5))) + if (!(fields=cli_read_rows(mysql,(MYSQL_FIELD*)0, protocol_41(mysql) ? 7:5))) DBUG_RETURN(1); if (!(mysql->fields=unpack_fields(fields,&mysql->field_alloc, (uint) field_count,0, @@ -2483,7 +2655,7 @@ get_info: DBUG_RETURN(1); mysql->status= MYSQL_STATUS_GET_RESULT; mysql->field_count= (uint) field_count; - mysql->warning_count= 0; + DBUG_PRINT("exit",("ok")); DBUG_RETURN(0); } @@ -2683,6 +2855,25 @@ mysql_fetch_row(MYSQL_RES *res) } +/************************************************************************** + Get column lengths of the current row + If one uses mysql_use_result, res->lengths contains the length information, + else the lengths are calculated from the offset between pointers. +**************************************************************************/ + +ulong * STDCALL +mysql_fetch_lengths(MYSQL_RES *res) +{ + MYSQL_ROW column; + + if (!(column=res->current_row)) + return 0; /* Something is wrong */ + if (res->data) + (*res->methods->fetch_lengths)(res->lengths, column, res->field_count); + return res->lengths; +} + + int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) { @@ -2751,6 +2942,18 @@ mysql_options(MYSQL *mysql,enum mysql_option option, const char *arg) case MYSQL_SECURE_AUTH: mysql->options.secure_auth= *(my_bool *) arg; break; + case MYSQL_REPORT_DATA_TRUNCATION: + mysql->options.report_data_truncation= test(*(my_bool *) arg); + break; + case MYSQL_OPT_RECONNECT: + mysql->reconnect= *(my_bool *) arg; + break; + case MYSQL_OPT_SSL_VERIFY_SERVER_CERT: + if (!arg || test(*(uint*) arg)) + mysql->options.client_flag|= CLIENT_SSL_VERIFY_SERVER_CERT; + else + mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT; + break; default: DBUG_RETURN(1); } @@ -2779,8 +2982,82 @@ uint STDCALL mysql_errno(MYSQL *mysql) return mysql->net.last_errno; } + const char * STDCALL mysql_error(MYSQL *mysql) { return mysql->net.last_error; } + +/* + Get version number for server in a form easy to test on + + SYNOPSIS + mysql_get_server_version() + mysql Connection + + EXAMPLE + 4.1.0-alfa -> 40100 + + NOTES + We will ensure that a newer server always has a bigger number. + + RETURN + Signed number > 323000 +*/ + +ulong STDCALL +mysql_get_server_version(MYSQL *mysql) +{ + uint major, minor, version; + char *pos= mysql->server_version, *end_pos; + major= (uint) strtoul(pos, &end_pos, 10); pos=end_pos+1; + minor= (uint) strtoul(pos, &end_pos, 10); pos=end_pos+1; + version= (uint) strtoul(pos, &end_pos, 10); + return (ulong) major*10000L+(ulong) (minor*100+version); +} + + +/* + mysql_set_character_set function sends SET NAMES cs_name to + the server (which changes character_set_client, character_set_result + and character_set_connection) and updates mysql->charset so other + functions like mysql_real_escape will work correctly. +*/ +int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name) +{ + struct charset_info_st *cs; + const char *save_csdir= charsets_dir; + + if (mysql->options.charset_dir) + charsets_dir= mysql->options.charset_dir; + + if (strlen(cs_name) < MY_CS_NAME_SIZE && + (cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0)))) + { + char buff[MY_CS_NAME_SIZE + 10]; + charsets_dir= save_csdir; + /* Skip execution of "SET NAMES" for pre-4.1 servers */ + if (mysql_get_server_version(mysql) < 40100) + return 0; + sprintf(buff, "SET NAMES %s", cs_name); + if (!mysql_real_query(mysql, buff, strlen(buff))) + { + mysql->charset= cs; + } + } + else + { + char cs_dir_name[FN_REFLEN]; + get_charsets_dir(cs_dir_name); + mysql->net.last_errno= CR_CANT_READ_CHARSET; + strmov(mysql->net.sqlstate, unknown_sqlstate); + my_snprintf(mysql->net.last_error, sizeof(mysql->net.last_error) - 1, + ER(mysql->net.last_errno), cs_name, cs_dir_name); + + } + charsets_dir= save_csdir; + return mysql->net.last_errno; +} + + diff --git a/sql-common/my_time.c b/sql-common/my_time.c index 3c46a944ba9..93bf23ed284 100644 --- a/sql-common/my_time.c +++ b/sql-common/my_time.c @@ -47,6 +47,64 @@ uchar days_in_month[]= {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0}; static long my_time_zone=0; +/* Calc days in one year. works with 0 <= year <= 99 */ + +uint calc_days_in_year(uint year) +{ + return ((year & 3) == 0 && (year%100 || (year%400 == 0 && year)) ? + 366 : 365); +} + +/* + Check datetime value for validity according to flags. + + SYNOPSIS + check_date() + ltime Date to check. + not_zero_date ltime is not the zero date + flags flags to check + was_cut set to 2 if value was truncated. + NOTE: This is not touched if value was not truncated + NOTES + Here we assume that year and month is ok ! + If month is 0 we allow any date. (This only happens if we allow zero + date parts in str_to_datetime()) + Disallow dates with zero year and non-zero month and/or day. + + RETURN + 0 ok + 1 error +*/ + +static my_bool check_date(const MYSQL_TIME *ltime, my_bool not_zero_date, + ulong flags, int *was_cut) +{ + if (not_zero_date) + { + if ((((flags & TIME_NO_ZERO_IN_DATE) || !(flags & TIME_FUZZY_DATE)) && + (ltime->month == 0 || ltime->day == 0)) || + (!(flags & TIME_INVALID_DATES) && + ltime->month && ltime->day > days_in_month[ltime->month-1] && + (ltime->month != 2 || calc_days_in_year(ltime->year) != 366 || + ltime->day != 29)) || + (ltime->year == 0 && (ltime->month != 0 || ltime->day != 0))) + { + *was_cut= 2; + return TRUE; + } + } + else if (flags & TIME_NO_ZERO_DATE) + { + /* + We don't set *was_cut here to signal that the problem was a zero date + and not an invalid date + */ + return TRUE; + } + return FALSE; +} + + /* Convert a timestamp string to a MYSQL_TIME value. @@ -58,8 +116,12 @@ static long my_time_zone=0; flags Bitmap of following items TIME_FUZZY_DATE Set if we should allow partial dates TIME_DATETIME_ONLY Set if we only allow full datetimes. - was_cut Set to 1 if value was cut during conversion or to 0 - otherwise. + TIME_NO_ZERO_IN_DATE Don't allow partial dates + TIME_NO_ZERO_DATE Don't allow 0000-00-00 date + TIME_INVALID_DATES Allow 2000-02-31 + was_cut 0 Value ok + 1 If value was cut during conversion + 2 Date part was within ranges but date was wrong DESCRIPTION At least the following formats are recogniced (based on number of digits) @@ -104,11 +166,11 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS]; uint add_hours= 0, start_loop; ulong not_zero_date, allow_space; - bool is_internal_format; + my_bool is_internal_format; const char *pos, *last_field_pos; const char *end=str+length; const uchar *format_position; - bool found_delimitier= 0, found_space= 0; + my_bool found_delimitier= 0, found_space= 0; uint frac_pos, frac_len; DBUG_ENTER("str_to_datetime"); DBUG_PRINT("ENTER",("str: %.*s",length,str)); @@ -349,8 +411,7 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, if (number_of_fields < 3 || l_time->year > 9999 || l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 || - l_time->minute > 59 || l_time->second > 59 || - (!(flags & TIME_FUZZY_DATE) && (l_time->month == 0 || l_time->day == 0))) + l_time->minute > 59 || l_time->second > 59) { /* Only give warning for a zero date if there is some garbage after */ if (!not_zero_date) /* If zero date */ @@ -364,11 +425,13 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time, } } } - if (not_zero_date) - *was_cut= 1; + *was_cut= test(not_zero_date); goto err; } + if (check_date(l_time, not_zero_date, flags, was_cut)) + goto err; + l_time->time_type= (number_of_fields <= 3 ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME); @@ -414,12 +477,12 @@ err: 1 error */ -bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, - int *was_cut) +my_bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, + int *was_cut) { long date[5],value; const char *end=str+length, *end_of_days; - bool found_days,found_hours; + my_bool found_days,found_hours; uint state; l_time->neg=0; @@ -602,7 +665,7 @@ void init_time(void) time_t seconds; struct tm *l_time,tm_tmp; MYSQL_TIME my_time; - bool not_used; + my_bool not_used; seconds= (time_t) time((time_t*) 0); localtime_r(&seconds,&tm_tmp); @@ -668,7 +731,8 @@ long calc_daynr(uint year,uint month,uint day) Time in UTC seconds since Unix Epoch representation. */ my_time_t -my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap) +my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, + my_bool *in_dst_time_gap) { uint loop; time_t tmp; @@ -832,3 +896,172 @@ int my_TIME_to_str(const MYSQL_TIME *l_time, char *to) return 0; } } + + +/* + Convert datetime value specified as number to broken-down TIME + representation and form value of DATETIME type as side-effect. + + SYNOPSIS + number_to_datetime() + nr - datetime value as number + time_res - pointer for structure for broken-down representation + flags - flags to use in validating date, as in str_to_datetime() + was_cut 0 Value ok + 1 If value was cut during conversion + 2 Date part was within ranges but date was wrong + + DESCRIPTION + Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS, + YYYYMMDDHHMMSS to broken-down TIME representation. Return value in + YYYYMMDDHHMMSS format as side-effect. + + This function also checks if datetime value fits in DATETIME range. + + RETURN VALUE + -1 Timestamp with wrong values + anything else DATETIME as integer in YYYYMMDDHHMMSS format + Datetime value in YYYYMMDDHHMMSS format. +*/ + +longlong number_to_datetime(longlong nr, MYSQL_TIME *time_res, + uint flags, int *was_cut) +{ + long part1,part2; + + *was_cut= 0; + + if (nr == LL(0) || nr >= LL(10000101000000)) + goto ok; + if (nr < 101) + goto err; + if (nr <= (YY_PART_YEAR-1)*10000L+1231L) + { + nr= (nr+20000000L)*1000000L; /* YYMMDD, year: 2000-2069 */ + goto ok; + } + if (nr < (YY_PART_YEAR)*10000L+101L) + goto err; + if (nr <= 991231L) + { + nr= (nr+19000000L)*1000000L; /* YYMMDD, year: 1970-1999 */ + goto ok; + } + if (nr < 10000101L) + goto err; + if (nr <= 99991231L) + { + nr= nr*1000000L; + goto ok; + } + if (nr < 101000000L) + goto err; + if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959)) + { + nr= nr+LL(20000000000000); /* YYMMDDHHMMSS, 2000-2069 */ + goto ok; + } + if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000)) + goto err; + if (nr <= LL(991231235959)) + nr= nr+LL(19000000000000); /* YYMMDDHHMMSS, 1970-1999 */ + + ok: + part1=(long) (nr/LL(1000000)); + part2=(long) (nr - (longlong) part1*LL(1000000)); + time_res->year= (int) (part1/10000L); part1%=10000L; + time_res->month= (int) part1 / 100; + time_res->day= (int) part1 % 100; + time_res->hour= (int) (part2/10000L); part2%=10000L; + time_res->minute=(int) part2 / 100; + time_res->second=(int) part2 % 100; + + if (time_res->year <= 9999 && time_res->month <= 12 && + time_res->day <= 31 && time_res->hour <= 23 && + time_res->minute <= 59 && time_res->second <= 59 && + !check_date(time_res, (nr != 0), flags, was_cut)) + return nr; + + /* Don't want to have was_cut get set if NO_ZERO_DATE was violated. */ + if (!nr && (flags & TIME_NO_ZERO_DATE)) + return LL(-1); + + err: + *was_cut= 1; + return LL(-1); +} + + +/* Convert time value to integer in YYYYMMDDHHMMSS format */ + +ulonglong TIME_to_ulonglong_datetime(const MYSQL_TIME *time) +{ + return ((ulonglong) (time->year * 10000UL + + time->month * 100UL + + time->day) * ULL(1000000) + + (ulonglong) (time->hour * 10000UL + + time->minute * 100UL + + time->second)); +} + + +/* Convert TIME value to integer in YYYYMMDD format */ + +ulonglong TIME_to_ulonglong_date(const MYSQL_TIME *time) +{ + return (ulonglong) (time->year * 10000UL + time->month * 100UL + time->day); +} + + +/* + Convert TIME value to integer in HHMMSS format. + This function doesn't take into account time->day member: + it's assumed that days have been converted to hours already. +*/ + +ulonglong TIME_to_ulonglong_time(const MYSQL_TIME *time) +{ + return (ulonglong) (time->hour * 10000UL + + time->minute * 100UL + + time->second); +} + + +/* + Convert struct TIME (date and time split into year/month/day/hour/... + to a number in format YYYYMMDDHHMMSS (DATETIME), + YYYYMMDD (DATE) or HHMMSS (TIME). + + SYNOPSIS + TIME_to_ulonglong() + + DESCRIPTION + The function is used when we need to convert value of time item + to a number if it's used in numeric context, i. e.: + SELECT NOW()+1, CURDATE()+0, CURTIMIE()+0; + SELECT ?+1; + + NOTE + This function doesn't check that given TIME structure members are + in valid range. If they are not, return value won't reflect any + valid date either. +*/ + +ulonglong TIME_to_ulonglong(const MYSQL_TIME *time) +{ + switch (time->time_type) { + case MYSQL_TIMESTAMP_DATETIME: + return TIME_to_ulonglong_datetime(time); + case MYSQL_TIMESTAMP_DATE: + return TIME_to_ulonglong_date(time); + case MYSQL_TIMESTAMP_TIME: + return TIME_to_ulonglong_time(time); + case MYSQL_TIMESTAMP_NONE: + case MYSQL_TIMESTAMP_ERROR: + return ULL(0); + default: + DBUG_ASSERT(0); + } + return 0; +} + diff --git a/sql-common/my_user.c b/sql-common/my_user.c new file mode 100644 index 00000000000..c39f08e520f --- /dev/null +++ b/sql-common/my_user.c @@ -0,0 +1,57 @@ +/* Copyright (C) 2005 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; either version 2 of the License, or + (at your option) any later version. + + 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 */ + +#include <my_user.h> +#include <m_string.h> + + +/* + Parse user value to user name and host name parts. + + SYNOPSIS + user_id_str [IN] User value string (the source). + user_id_len [IN] Length of the user value. + user_name_str [OUT] Buffer to store user name part. + Must be not less than USERNAME_LENGTH + 1. + user_name_len [OUT] A place to store length of the user name part. + host_name_str [OUT] Buffer to store host name part. + Must be not less than HOSTNAME_LENGTH + 1. + host_name_len [OUT] A place to store length of the host name part. +*/ + +void parse_user(const char *user_id_str, uint user_id_len, + char *user_name_str, uint *user_name_len, + char *host_name_str, uint *host_name_len) +{ + char *p= strrchr(user_id_str, '@'); + + if (!p) + { + *user_name_len= 0; + *host_name_len= 0; + } + else + { + *user_name_len= p - user_id_str; + *host_name_len= user_id_len - *user_name_len - 1; + + memcpy(user_name_str, user_id_str, *user_name_len); + memcpy(host_name_str, p + 1, *host_name_len); + } + + user_name_str[*user_name_len]= 0; + host_name_str[*host_name_len]= 0; +} |