Перейти к содержимому

DustBlue IPB Skin by CodeGame Networks

  • Авторизуйтесь для ответа в теме
В этой теме нет ответов

#1 microskope

microskope

    Новичок

  • Пользователи
  • Pip
  • Репутация
    0
  • 4 сообщений

Отправлено 05 July 2018 - 18:47

Данная статья не претендует на звание "учебник года" или "самый лучший пример", но необходимые знания для связи и работы с MySQL из С++ - извлечь можно.

Текст статьи разбит на три части - теория, реализация, примеры. К сожалению без теории никуда... однако Я постараюсь представить все самое необходимое, в кратце, доступно и понятно. Приступим...

Что необходимо для того, чтобы разработать приложение, которое будет взаимодействовать с базой данных mysql? Все очень просто - вам необходимо иметь сам mysql, а точнее статическую библиотеку libmysql.lib, которая поставляется вместе с самой бд. На самом деле существует две библиотеки libmysql.lib - одна для отладки, другая для релиза. В винде они находятся в mysql/lib/opt и mysql/lib/debug. Кроме библиотеки понадобятся заголовочные файлы, которые находятся в mysql/include. Приложение будем разрабатывать в MS VS, как не странно - на С++. На самом деле не важно где разрабатывать, главное знать - для того, что бы приложение могло работать с базой данных mysql нужно следующее:

1) подключить дополнительные инклюды - mysql/include
2) подключить дополнительную статическую библиотеку - mysql/lib/opt/libmysql.lib
Другими словами - компилируете приложение с инклюдами mysql/include , линкуете с mysql/lib/opt/libmysql.lib . Существует ещё один факт, который Вам необходимо знать. Если Вы установили MySQL с инклюдами для девелоперов и документацией, то у Вас открывается незабываемая возможность стать читателем одного из важных разделов в мануале по MySQL - manual.chm -> API and Libraries -> MySQL C API.

Раздел MySQL C++ API представлен очень смутно, а точнее - он ссылается на готовое решение MySQL++. Если хотите - можете порыться в Интернете на эту тему. Но нас интересует другое - мы хотим изобрести свой велосипед, по этому приступаем к MySQL C API. Коротко и ясно...

Самая главная структура - MYSQL. Как пишут в мануале - структура представляет хэндл соединения с базой данных; другими словами - это то, без чего вы не сделаете ни одного SQL запроса и соответственно не получите результат. Запомните следующее: для одной базы данных (одного соединения) - своя копия структуры MYSQL, в единственном экземпляре - это важно. По большому счёту - эта структура используется практически во всех MySQL Си-шных функциях (так пишут в мане и я им верю, советую верить и Вам :).

Не менее важная структура - MYSQL_RES. Если Вы хоть раз работали с MySQL и (по идее... я надеюсь) делали запросы типа SELECT, SHOW, DESC, то наверняка у Вас был некий результат в виде колонок и ячеек. Предназначение структуры - хранение результата выполненого запроса, для дальнейшей его обработки.

MYSQL_ROW. Результат запроса SELECT, SHOW, DESC представен в виде полей с ячейками. В ячейках находятся некоторые значения. Вот представление значений этих ячеек, если быть точным - самих ячеек - можно изобразить в виде массива MYSQL_ROW. Если верить (а мы заранее верим) мануалу, то это безопасный массив типа строки (не заоверфлоувите). Все данные, которые были извлечены запросом - представленны в виде массива строк MYSQL_ROW.

MYSQL_FIELD. Так как ячейки мы получаем в виде строк, хотелось бы знать что они из себя представляют - это цифра? а может быть это файл? или булево значение... Если Вы не в курсе, из каких типов полей состоит запрашиваемая таблица - эта структура для Вас. Более точное представление структуры Вы можете прочесть в мане. Я отмечу только самые интересные поля: char * name - имя поля; unsigned long length - размер поля (пр. varchar(102) - length = 102); enum enum_field_types type - тип поля (MYSQL_TYPE_LONG - INTEGER, MYSQL_TYPE_VAR_STRING - VARCHAR, и т.д. по ману).

Основные структуры - есть. Дело остается только за основными функциями (основные, но не все... необходимый рацион для работы нижеприведенных примеров). Коротко и ясно...

MYSQL *mysql_init(MYSQL *mysql) - Инициализация. Закидываете в качестве параметра ссылку на вашу структуру MYSQL и получаете её же на выходе. Эту функцию необходимо запускать самой первой, прежде чем работать с остальными функциями mysql-а. Если инициализация прошла неудачно - вы получите на выходе NULL, соответственно дальше нет смысла жить (приложению ;).

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag) - соединяет Вас с MySQL сервером. Думаю назначение параметров можно понять по их именам. Некоторые особенности: в качестве host'а Вы можете использовать пайпы (pipes/каналы), если сервер разрешает и поддерживает такие соединения; пароль шифровать нельзя, это делает функция.

void mysql_close(MYSQL *mysql) - тут нечего объяснять. Закрываем соединение. Освобождаем mysql.

int mysql_real_query(MYSQL *mysql, const char *query, unsigned long length) - выполнение SQL запроса на сервере. query - запрос в виде строки, length - длина строки запроса в байтах. Функция выполнилась успешно - результат выполнения == 0.

MYSQL_RES *mysql_store_result(MYSQL *mysql) - Эту функцию необходимо запускать после каждого удачного выполнения SQL запроса выборок (SELECT, SHOW, DESCRIBE, т.д.). Напомню, SQL запросы мы выполняем с помощью функции mysql_real_query. Запускать функцию mysql_store_result необходимо для того, чтобы извлечь данные, которые были получены после выполнения запроса. Существует так же и другая функция с аналогичной задачей - mysql_use_result. Разница заключается в следующем: store_result считывает результат выборки в клиент (тоесть, нам становится известно полное содержание результата); use_result работает наоборот - полный размер результата не известен (как пишут в мане - эта функция работает быстрее mysql_store_result). Существуют так же и другие отличия, которые Вы сможете отметить для себя, когда попробуете заменить store_result на use_result.

void mysql_free_result(MYSQL_RES *result) - освобождает память, выделенную под результат с помощью функций mysql_store_result(), mysql_use_result().

my_ulonglong mysql_num_rows(MYSQL_RES *result) - возвращает количество строк в полученном результате.

unsigned int mysql_num_fields(MYSQL_RES *result) - возвращает количество столбцов в полученном результате.

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result). С помощью этой функции мы извлекаем из нашего результата result строки, поочередно - в цикле. Как видно из описания функции мы получаем данные в виде структуры MYSQL_ROW. Возвращаемая структура представляет из себя массив строк. Каждый элемент массива это ячейка, представленная в текущей строке выборки (результата).

unsigned long *mysql_fetch_lengths(MYSQL_RES *result). Данная функция необходима для того, чтобы иметь представление о длине строки каждой ячейки массива выборки (о как). Другими словами... результат выполнения этой функции - это массив чисел, элементы которого содержат длины строки в каждой ячейке текущего элемента выборки (строки в таблице). Реально функция делает следующее: создает массив, в который запихивает strlen (длину) каждой ячейки. Предназначение поймете из примера.

Вот вроде бы и все, что необходимо иметь у себя в арсенале для реализации простого приложения, которое будет работать с MySQL. Ещё немного теории и приступим к примерам...

Принцип работы MySQL клиента заключается в следующем:

1) инициализируем структуру MYSQL для работы с базой
2) соединяемся с MySQL сервером (выбираем базу данных)
3) выполняем запрос (например SHOW TABLES)
4) выделяем место для результата всей выборки (все поля (строки))
5) обрабатываем полученные данные
6) освобаждаем выделенное место результата
7) закрываем соединение с сервером
Приступим к практике. В качестве примера я предлагаю создать некоторый класс, который будет работать с базой данных. Объект такого класса сможет выполнять любой запрос, получать результат в зависимости от посланного запроса, а так же выделять и освобождать выделенное в процессе работы место в памяти. Так как моя гордость не позволяет мне выкладывать в паблик свежии версии своих исходных кодов, Я поделюсь самой первой версией такого класса... код будет немного смешным (: но работает как надо.

CPPMySQL.h

#include <my_global.h>
#include <mysql.h>
#include <stdarg.h>

#define MYSQLE_INITIALIZчтоION_ERROR 0x001
#define MYSQLE_CONNECT_ERROR 0x002
#define MYSQLE_UNKNOWN_ERROR 0x003

class CPPMySQLError{
public:
// constructor , first param - error code
CPPMySQLError(DWORD code);
// get error code
DWORD GetErrorCode();
// get some text about error
CHAR * GetErrorTrace();
privчтоe:
// error code
DWORD ErrorCode;
// error text
CHAR ErrorTrace[1024];
};

struct mysql_row{
// fields count
int fields;
// columns count
int cols;
// row representчтоion
struct _row{
// dчтоa in row
char * dчтоa;
// length of dчтоa
int length;
}*row;
// convert row dчтоa in integer
int asInt(int index){return чтоoi(row[index].dчтоa);};
};

class CPPMySQL{
public:
// constructor - connect to server
CPPMySQL(const char * host, const char * user, const char * password) throw (CPPMySQLError);
// constructor - connect to server, select db
CPPMySQL(const char * host, const char * user, const char * password, const char * dbname) throw (CPPMySQLError);
// destructor - destroy connection
~CPPMySQL();
// whчто was last sql?
char * get_last_sql(){return this->last_sql;};
// sql query to server
int query(const char * sql,...);
// fetch rows from last sql query
mysql_row * fetch(int query_result) throw (CPPMySQLError);
// free result rows memory (stчтоic member)
stчтоic void free_rows(mysql_row * rows);
privчтоe:
// MYSQL structure
MYSQL mysql;
// last sql
char last_sql[512];
// make char escape, example: ' to '
char * escape(const char *stчтоement);
};
CPPMySQL.cpp

#include "CPPMysql.h"

/* Constructor initializчтоion */
CPPMySQLError::CPPMySQLError(DWORD code){
this->ErrorCode = code;
memset(this->ErrorTrace,0,sizeof this->ErrorTrace);
sprintf(this->ErrorTrace,"MySQL error (0x%X)",code);
}

/* Return error code value */
DWORD CPPMySQLError::GetErrorCode(){
return this->ErrorCode;
}

/* Return error informчтоion */
CHAR * CPPMySQLError::GetErrorTrace(){
return this->ErrorTrace;
}

/* Constructors */
CPPMySQL::CPPMySQL(const char * host, const char * user, const char * password) throw (CPPMySQLError){
// init this->mysql struct
// if failed - throw error with code MYSQLE_INITIALIZчтоION_ERROR
if (!mysql_init(&this->mysql)) throw CPPMySQLError(MYSQLE_INITIALIZчтоION_ERROR);
// connect to MySQL server
// if failed - throw error with code MYSQLE_CONNECT_ERROR
if (!mysql_real_connect(&this->mysql,host,user,password,NULL,0,NULL,0)) throw CPPMySQLError(MYSQLE_CONNECT_ERROR);
}

CPPMySQL::CPPMySQL(const char * host, const char * user, const char * password, const char * dbname) throw (CPPMySQLError){
if (!mysql_init(&this->mysql)) throw CPPMySQLError(MYSQLE_INITIALIZчтоION_ERROR);
// connect to MySQL server, select db
if (!mysql_real_connect(&this->mysql,host,user,password,dbname,0,NULL,0)) throw CPPMySQLError(MYSQLE_CONNECT_ERROR);
}

/* Destructor */
CPPMySQL::~CPPMySQL(){
// close connection
// free this->mysql
mysql_close(&this->mysql);
}

/*
Sends sql query to the server
If there is any parameters after sql stчтоement:
- check out this params and make some chages in sql query
- change %d to digit parameter, %s to string, %c to char, %f to floчто
Stupid code (:
TODO: if there is no params after sql - just do the fucking query

Return value: not null - all ok
*/
int CPPMySQL::query(const char *sql,...){
va_list vl;
va_start(vl,sql);
int sql_len = strlen(sql), i = 0, j = 0, a = 0, len = 0;
int new_len = sql_len;
char temp[64];
char * string;
/* count new length */
while (i < sql_len){
if (sql[i++] == '%' && i != sql_len){
memset(temp,'�',64);
switch (sql[i]){
case 's' :
new_len += strlen(this->escape(va_arg(vl, char*))) - 2;
break;
case 'd' :
sprintf(temp,"%d",va_arg(vl,int));
new_len += strlen(temp) - 2;
break;
case 'c' :
sprintf(temp,"%c",va_arg(vl,char));
string = this->escape(temp);
new_len += strlen(string) - 2;
break;
case 'f' :
sprintf(temp,"%f",va_arg(vl,floчто));
new_len += strlen(temp) - 2;
break;
}
}
}
va_end(vl);
char * new_sql = new char[new_len + 1];
memset(new_sql,'�',new_len + 1);
va_start(vl,sql);
i = 0;
/* make new sql with changes */
while (i < sql_len){
if (sql[i++] == '%' && i != sql_len){
memset(temp,'�',64);
switch (sql[i++]){
case 's' :
string = this->escape(va_arg(vl,char*));
len = strlen(string);
for (a=0; a<len; a++)
new_sql[j++] = string[a];
break;
case 'd' :
sprintf(temp,"%d",va_arg(vl,int));
len = strlen(temp);
for (a=0; a<len; a++)
new_sql[j++] = temp[a];
break;
case 'c' :
sprintf(temp,"%c",va_arg(vl,char));
string = this->escape(temp);
len = strlen(string);
for (a=0; a<len; a++)
new_sql[j++] = string[a];
break;
case 'f' :
sprintf(temp,"%f",va_arg(vl,floчто));
len = strlen(temp);
for (a=0; a<len; a++)
new_sql[j++] = temp[a];
break;
default:
new_sql[j++] = sql[i - 1];
}
}else new_sql[j++] = sql[i - 1];
}
va_end(vl);
memset(this->last_sql,0,sizeof this->last_sql);
memcpy(this->last_sql,new_sql,new_len);
// go query, go!
int result = mysql_real_query(&this->mysql,new_sql,new_len);
// we don't need you any more...
delete new_sql;
return result;
}

/*
We have result from query method
We want to get our fields and rows

Return value: all ok - mysql_row array , or null if there was error/no rows
There is exceptions when something goes wrong...
*/
mysql_row * CPPMySQL::fetch(int query_result) throw (CPPMySQLError){
if (query_result) return NULL;
MYSQL_RES * result = mysql_store_result(&this->mysql);
if (result == NULL) throw CPPMySQLError(MYSQLE_UNKNOWN_ERROR);
int num_rows = mysql_num_rows(result);
if (!num_rows){
mysql_free_result(result);
return NULL;
}
MYSQL_ROW real_row;
int num_cols = mysql_num_fields(result);
unsigned long * lengths;
// our new mysql_row array
mysql_row * rows = new mysql_row[num_rows];
/* copy result from MYSQL_ROW to our mysql_row */
for (int i=0; i<num_rows; i++){
rows[i].fields = num_rows;
rows[i].cols = num_cols;
rows[i].row = new mysql_row::_row[num_cols];
real_row = mysql_fetch_row(result);
if (real_row == NULL){
mysql_free_result(result);
throw CPPMySQLError(MYSQLE_UNKNOWN_ERROR);
}
lengths = mysql_fetch_lengths(result);
for (int j=0; j<num_cols; j++){
rows[i].row[j].length = (int)lengths[j];
rows[i].row[j].dчтоa = new char[(int)lengths[j] + 1];
memset(rows[i].row[j].dчтоa,0,(int)lengths[j] + 1);
memcpy(rows[i].row[j].dчтоa,real_row[j],(int)lengths[j]);
}
}
mysql_free_result(result);
return rows;
}

/*
When we don't need mysql_row array result any more:
drop result, by memory free (:
*/
void CPPMySQL::free_rows(mysql_row * rows){
if (rows == NULL) return;
for (int i=0; i<rows->fields; i++){
for (int j=0; j<rows->cols; j++)
delete rows[i].row[j].dчтоa;
delete [] rows[i].row;
}
delete [] rows;
}

/*
SQL-Injection... go away? :D
*/
char * CPPMySQL::escape(const char *stчтоement){
char temp[1024];
mysql_real_escape_string(&this->mysql,temp,stчтоement,strlen(stчтоement));
return temp;
}
Пример:

#include <windows.h>
#include "CPPMySQL.h"

void main(int argc, char * args[]){
// Объявляем объект mysql, с помощью которого будем работать с базой
// Инициализируем как NULL
CPPMySQL * mysql = NULL;
// пробуем запуститься
try{
printf("connect to server...");
// Выделяем место для нашего объекта mysql
// Соединяемся с localhost под логином tester, без пароля
// Выбираем базу данных test
mysql = new MySQL("localhost","tester","","test");
printf("okn");
printf("creчтоe table test...");
// Выполняем запрос - создаем таблицу test
if (!mysql->query("creчтоe table test(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, tkey VARCHAR(128), tvalue VARCHAR(255))")){
// Запрос не выполнился
printf("error");
// Освобождаем место, занятое объектом mysql
delete mysql;
return;
}
printf("do next queries:n");
// Выполняем запросы в цикле (10 раз)
for (int i=0; i<10; i++){
printf("insert into test values(NULL,"%s","value-%d")...","key",i*2);
/*
Выполняем запрос, метод mysql->query совершит замену в запросе
"insert into test values(NULL,"%s","value-%d")"
следующим образом:
%s = "key"
%d = i*2
*/
if (!mysql->query("insert into test values(NULL,"%s","value-%d")","key",i*2)) printf("okn");
else{
// Запрос не выполнился
printf("errorn");
// Освобождаем место, занятое объектом mysql
delete mysql;
return;
}
}
printf("do select:n");
// Выполняем запрос выборки в базе
// Передаем результат запроса mysql->query в mysql->fetch
// mysql->fetch возвращает массив ячеек, который присвается в rows
mysql_row * rows = mysql->fetch(mysql->query("select id, tvalue from test order by id desc limit 0,5"));
if (rows == NULL){
// Либо ячеек не было, либо запрос был не успешным
printf("wtf? no rows?");
delete mysql;
return;
}
// Делаем распечатку полученных ячеек
// rows[i] - строка
// rows[i].row[j] - столбец
for (int i=0; i<rows[0].fields; i++){
for (int j=0; j<rows[0].cols; j++)
printf("%st",rows[i].row[j].dчтоa);
printf("n");
}
// Результат выборки (rows) более нам не нужен - освобождаем занятое место в памяти
CPPMySQL::free_rows(rows);
printf("drop table test...");
// Выполняем запрос - дропнуть таблицу test
if (!mysql->query("drop table test")) printf("okn");
else printf("errorn");
// Закрываем соединение, освобождаем место...
delete mysql;
// словили нашу (CPPMySQLError) ошибку где-то
}cчтоch (CPPMySQLError error){
// печатаем ошибку
printf("MySQL error: %sn", error.GetErrorTrace());
// если было установленно соединение - закрываем
if (mysql != NULL) delete mysql;
return;
}
}





Темы с аналогичным тегами криминальные новости экономики, новости хакинга, новости хакеров, новости в мире кардинга, статьи по кардингу, стаьи по безопасности, кардинг статьи, новости кардинг экономики, мировые новости, новости кардинга

Количество пользователей, читающих эту тему: 0

0 пользователей, 0 гостей, 0 анонимных