76.蛤蟆笔记 MySQL学习——C编写MySQL程序九预处理
欢迎转载,转载请标明出处:http://blog.csdn.net/notbaron/article/details/51078586
之前都是以字符串的形式发送和检索所有信息。也可以使用二进制的客户端/服务器协议。
二进制协议支持服务器端预处理语句,并且可以按本地格式来传输数据值。
并非所有的语句都能被预处理,使用如下几种语句:create table,delete,do,insert,replace,select,set,update和绝大多数show变体。
预处理基本流程如下:
l Mysql_stmt_init分配一个语句处理器,返回一个指向处理器的指针
l 调用mysql_stms_prepare把语句发送到服务器,接受预处理,与语句处理器相关联
l 如果包含占位符,在执行之前,为每个占位符提供相应的数据
l 调用mysql_stmt_execute函数,执行该语句
l 如果只修改不生产结果集,调用mysql_stmt_affected_rows来确定影响的行数
l 如果有结果集,调用mysql_stmt_result_metadata来获得关于该结果集的元数据。调用mysql_stmt_bind_result把这些结构绑定到语句处理器上,然后反复调用mysql_stmt_fetch,依次取回每一行。
l 使用完语句处理之后,调用ysql_stmt_close来释放它。
#include <my_global.h>
#include <my_sys.h>
#include <m_string.h> /* for strdup() */
#include <mysql.h>
#include <my_getopt.h>
#ifdef HAVE_OPENSSL
enum options_client
{
OPT_SSL_SSL=256,
OPT_SSL_KEY,
OPT_SSL_CERT,
OPT_SSL_CA,
OPT_SSL_CAPATH,
OPT_SSL_CIPHER,
OPT_SSL_VERIFY_SERVER_CERT
};
#endif
static char *opt_host_name = NULL; /* server host (default=localhost) */
static char *opt_user_name = NULL; /* username (default=login name) */
static char *opt_password = NULL; /* password (default=none) */
static unsigned int opt_port_num = 0; /* port number(use built-in value) */
static char *opt_socket_name = NULL; /* socket name (use built-in value) */
static char *opt_db_name = NULL; /* database name (default=none) */
static unsigned int opt_flags = 0; /* connection flags (none) */
#include <sslopt-vars.h>
static int ask_password = 0; /* whether to solicit password */
static MYSQL *conn; /* pointer to connection handler*/
static const char *client_groups[] = {"client", NULL };
static struct my_option my_opts[] = /* option information structures */
{
{"help", '?', "Display this help and exit",
NULL, NULL,NULL,
GET_NO_ARG,NO_ARG, 0, 0, 0, 0, 0, 0},
{"host", 'h', "Host to connect to",
(uchar **)&opt_host_name, NULL, NULL,
GET_STR,REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"password", 'p', "Password",
(uchar **)&opt_password, NULL, NULL,
GET_STR,OPT_ARG, 0, 0, 0, 0, 0, 0},
{"port", 'P', "Port number",
(uchar **)&opt_port_num, NULL, NULL,
GET_UINT,REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"socket", 'S', "Socket path",
(uchar **)&opt_socket_name, NULL, NULL,
GET_STR,REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"user", 'u', "User name",
(uchar **)&opt_user_name, NULL, NULL,
GET_STR,REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
#include <sslopt-longopts.h>
{ NULL, 0,NULL, NULL, NULL, NULL, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0 }
};
/*
* Printdiagnostic message. If conn is non-NULL, print error information
* returned byserver.
*/
static void
print_error (MYSQL *conn, char *message)
{
fprintf(stderr, "%s\n", message);
if (conn !=NULL)
{
fprintf(stderr, "Error %u (%s): %s\n",
mysql_errno (conn), mysql_sqlstate (conn), mysql_error (conn));
}
}
/*
* Likeprint_error(), but use statement handler rather than
* connectionhandler to access error information.
*/
/* #@ _PRINT_STMT_ERROR_ */
static void
print_stmt_error (MYSQL_STMT *stmt, char *message)
{
fprintf(stderr, "%s\n", message);
if (stmt !=NULL)
{
fprintf(stderr, "Error %u (%s): %s\n",
mysql_stmt_errno (stmt),
mysql_stmt_sqlstate (stmt),
mysql_stmt_error (stmt));
}
}
/* #@ _PRINT_STMT_ERROR_ */
static my_bool
get_one_option (int optid, const struct my_option*opt, char *argument)
{
switch(optid)
{
case '?':
my_print_help (my_opts); /* printhelp message */
exit (0);
case'p': /* password */
if(!argument) /* no value given;solicit it later */
ask_password = 1;
else /* copy password,overwrite original */
{
opt_password = strdup (argument);
if (opt_password == NULL)
{
print_error (NULL, "could not allocate password buffer");
exit(1);
}
while(*argument)
*argument++ = 'x';
ask_password = 0;
}
break;
#include <sslopt-case.h>
}
return (0);
}
#include "process_prepared_statement.c"
int
main (int argc, char *argv[])
{
int opt_err;
MY_INIT(argv[0]);
load_defaults("my", client_groups, &argc, &argv);
if ((opt_err= handle_options (&argc, &argv, my_opts, get_one_option)))
exit(opt_err);
/* solicitpassword if necessary */
if(ask_password)
opt_password = get_tty_password (NULL);
/* getdatabase name if present on command line */
if (argc >0)
{
opt_db_name= argv[0];
--argc;++argv;
}
/* initializeclient library */
if(mysql_library_init (0, NULL, NULL))
{
print_error(NULL, "mysql_library_init() failed");
exit (1);
}
/* initializeconnection handler */
conn =mysql_init (NULL);
if (conn ==NULL)
{
print_error(NULL, "mysql_init() failed (probably out of memory)");
exit (1);
}
#ifdef HAVE_OPENSSL
/* pass SSLinformation to client library */
if(opt_use_ssl)
mysql_ssl_set (conn, opt_ssl_key, opt_ssl_cert, opt_ssl_ca,
opt_ssl_capath, opt_ssl_cipher);
mysql_options(conn,MYSQL_OPT_SSL_VERIFY_SERVER_CERT,
(char*)&opt_ssl_verify_server_cert);
#endif
/* connect toserver */
if(mysql_real_connect (conn, opt_host_name, opt_user_name, opt_password,
opt_db_name, opt_port_num, opt_socket_name, opt_flags) == NULL)
{
print_error(conn, "mysql_real_connect() failed");
mysql_close(conn);
exit (1);
}
process_prepared_statements (conn);
/* disconnectfrom server, terminate client library */
mysql_close(conn);
mysql_library_end ();
exit (0);
}
static void
insert_rows (MYSQL_STMT *stmt)
{
char *stmt_str = "INSERT INTO t (i,f,c,dt) VALUES(?,?,?,?)";
MYSQL_BIND param[4];
int my_int;
float my_float;
char my_str[26]; /* ctime() returns 26-character string */
MYSQL_TIME my_datetime;
unsigned long my_str_length;
time_t clock;
struct tm *cur_time;
int i;
printf("Inserting records...\n");
if(mysql_stmt_prepare (stmt, stmt_str, strlen (stmt_str)) != 0)
{
print_stmt_error (stmt, "Could not prepare INSERT statement");
return;
}
/*
* zero theparameter structures, then perform all parameter
*initialization that is constant and does not change for each row
*/
memset ((void*) param, 0, sizeof (param));
/* set up INTparameter */
param[0].buffer_type = MYSQL_TYPE_LONG;
param[0].buffer = (void *) &my_int;
param[0].is_unsigned = 0;
param[0].is_null = 0;
/*buffer_length, length need not be set */
/* set upFLOAT parameter */
param[1].buffer_type = MYSQL_TYPE_FLOAT;
param[1].buffer= (void *) &my_float;
param[1].is_null = 0;
/*is_unsigned, buffer_length, length need not be set */
/* set upCHAR parameter */
param[2].buffer_type = MYSQL_TYPE_STRING;
param[2].buffer = (void *) my_str;
param[2].buffer_length = sizeof (my_str);
param[2].is_null = 0;
/*is_unsigned need not be set, length is set later */
/* set upDATETIME parameter */
param[3].buffer_type = MYSQL_TYPE_DATETIME;
param[3].buffer = (void *) &my_datetime;
param[3].is_null= 0;
/*is_unsigned, buffer_length, length need not be set */
if(mysql_stmt_bind_param (stmt, param) != 0)
{
print_stmt_error (stmt, "Could not bind parameters forINSERT");
return;
}
for (i = 1; i<= 5; i++)
{
printf("Inserting record %d...\n", i);
(void) time(&clock); /* get current time */
/* set thevariables that are associated with each parameter */
/*param[0]: set my_int value */
my_int = i;
/*param[1]: set my_float value */
my_float =(float) i;
/*param[2]: set my_str to current ctime() string value */
/* and setlength to point to var that indicates my_str length */
(void)strcpy (my_str, ctime (&clock));
my_str[24]= '\0'; /* chop off trailing newline */
my_str_length = strlen (my_str);
param[2].length = &my_str_length;
/*param[3]: set my_datetime to current date and time components */
cur_time =localtime (&clock);
my_datetime.year = cur_time->tm_year + 1900;
my_datetime.month = cur_time->tm_mon + 1;
my_datetime.day = cur_time->tm_mday;
my_datetime.hour = cur_time->tm_hour;
my_datetime.minute = cur_time->tm_min;
my_datetime.second = cur_time->tm_sec;
my_datetime.second_part = 0;
my_datetime.neg= 0;
if(mysql_stmt_execute (stmt) != 0)
{
print_stmt_error (stmt, "Could not execute statement");
return;
}
sleep(1); /* pause briefly (to let the timechange) */
}
}
/* #@ _INSERT_RECORDS_ */
/* #@ _SELECT_RECORDS_ */
static void
select_rows (MYSQL_STMT *stmt)
{
char *stmt_str = "SELECT i, f, c, dt FROM t";
MYSQL_BIND param[4];
int my_int;
float my_float;
char my_str[24];
unsigned long my_str_length;
MYSQL_TIME my_datetime;
my_bool is_null[4];
printf("Retrieving records...\n");
if(mysql_stmt_prepare (stmt, stmt_str, strlen (stmt_str)) != 0)
{
print_stmt_error (stmt, "Could not prepare SELECT statement");
return;
}
if(mysql_stmt_field_count (stmt) != 4)
{
print_stmt_error (stmt, "Unexpected column count fromSELECT");
return;
}
/*
* initializethe result column structures
*/
memset ((void*) param, 0, sizeof (param)); /* zero the structures */
/* set up INTparameter */
param[0].buffer_type = MYSQL_TYPE_LONG;
param[0].buffer = (void *) &my_int;
param[0].is_unsigned = 0;
param[0].is_null = &is_null[0];
/*buffer_length, length need not be set */
/* set upFLOAT parameter */
param[1].buffer_type= MYSQL_TYPE_FLOAT;
param[1].buffer = (void *) &my_float;
param[1].is_null = &is_null[1];
/*is_unsigned, buffer_length, length need not be set */
/* set upCHAR parameter */
param[2].buffer_type = MYSQL_TYPE_STRING;
param[2].buffer= (void *) my_str;
param[2].buffer_length = sizeof (my_str);
param[2].length = &my_str_length;
param[2].is_null = &is_null[2];
/*is_unsigned need not be set */
/* set upDATETIME parameter */
param[3].buffer_type = MYSQL_TYPE_DATETIME;
param[3].buffer = (void *) &my_datetime;
param[3].is_null = &is_null[3];
/*is_unsigned, buffer_length, length need not be set */
if(mysql_stmt_bind_result (stmt, param) != 0)
{
print_stmt_error (stmt, "Could not bind parameters forSELECT");
return;
}
if(mysql_stmt_execute (stmt) != 0)
{
print_stmt_error (stmt, "Could not execute SELECT");
return;
}
/*
* fetchresult set into client memory; this is optional, but it
* enables mysql_stmt_num_rows() to be calledto determine the
* number ofrows in the result set.
*/
if(mysql_stmt_store_result (stmt) != 0)
{
print_stmt_error (stmt, "Could not buffer result set");
return;
}
else
{
/*mysql_stmt_store_result() makes row count available */
printf("Number of rows retrieved: %lu\n",
(unsigned long) mysql_stmt_num_rows (stmt));
}
while(mysql_stmt_fetch (stmt) == 0) /* fetcheach row */
{
/* displayrow values */
printf("%d ", my_int);
printf("%.2f ", my_float);
printf("%*.*s ", (int)my_str_length, (int) my_str_length, my_str);
printf("%04d-%02d-%02d %02d:%02d:%02d\n",
my_datetime.year,
my_datetime.month,
my_datetime.day,
my_datetime.hour,
my_datetime.minute,
my_datetime.second);
}
mysql_stmt_free_result (stmt); /* deallocate result set */
}
/* #@ _SELECT_RECORDS_ */
/* #@ _PROCESS_PREPARED_STATEMENTS_ */
void
process_prepared_statements (MYSQL *conn)
{
MYSQL_STMT *stmt;
char *use_stmt = "USE sampdb";
char *drop_stmt = "DROP TABLE IF EXISTS t";
char *create_stmt =
"CREATETABLE t (i INT, f FLOAT, c CHAR(24), dt DATETIME)";
/* selectdatabase and create test table */
if(mysql_query (conn, use_stmt) != 0
||mysql_query (conn, drop_stmt) != 0
||mysql_query (conn, create_stmt) != 0)
{
print_error(conn, "Could not set up test table");
return;
}
stmt = mysql_stmt_init(conn); /* allocate statement handler */
if (stmt ==NULL)
{
print_error(conn, "Could not initialize statement handler");
return;
}
/* insert andretrieve some records */
insert_rows(stmt);
select_rows(stmt);
mysql_stmt_close (stmt); /*deallocate statement handler */
}
Prepared.c文件包含process_prepared_statement.c,
主要的是调用process_prepared_statements函数。
process_prepared_statements先创建一个测试表。(需要存在数据库sampdb)
然后调用mysql_smst_init来分配一个预处理语句处理器,接着插入和检索多个行,最后释放处理器。
执行如下:
# ./a.out -h127.0.0.1 -u root
Inserting records...
Inserting record 1...
Inserting record 2...
Inserting record 3...
Inserting record 4...
Inserting record 5...
Retrieving records...
Number of rows retrieved: 5
1 1.00 Wed Apr 6 16:58:23 2016 2016-04-06 16:58:23
2 2.00 Wed Apr 6 16:58:24 2016 2016-04-06 16:58:24
3 3.00 Wed Apr 6 16:58:25 2016 2016-04-06 16:58:25
4 4.00 Wed Apr 6 16:58:26 2016 2016-04-06 16:58:26
5 5.00 Wed Apr 6 16:58:27 2016 2016-04-06 16:58:27