« Мелкие хитрости в отладке кода с помощью alert. Часть 1 | ЧПУ (Человеко-понятные УРЛы) в java » |
Работа с базой данных mysql из c++
Для того чтобы использовать возможности mysql сервера из c/c++ вам прежде всего необходимо в свойствах проекта указать местоположение папки include из каталога установки mysql, т.к. в нем находятся h-файлы с прототипами функций и типов данных. Также необходимо подключить к проекту .lib-файлы из каталога Lib.Методика дальнейшего программирования отличается и зависит от того, как именно вы хотите использовать средства баз данных в приложении: 1 – сервер mysql – является внешней программой и может быть использован для хранения данных нескольких клиентов, второй вариант – предполагает что движок сервера внедряется в код клиентского приложения и обслуживает только его. Начнем с первого варианта.
В коде клиента подключите только mysql.h. Перед ним обязательно должно быть подключен windows.h
#include <windows.h>
#include <mysql.h>
#define USE_OLD_FUNCTIONS
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <mysql.h>
MYSQL mysql;
MYSQL_RES *res;
MYSQL_ROW row;
void die(void){
printf("%s\n", mysql_error(&mysql));
exit(0);
}
void main(void){
unsigned int i = 0;
if (!mysql_init (&mysql)) abort ();
if (!(mysql_real_connect(&mysql,"localhost","root","", "kadry", 3306 , NULL , 0)))
die();
if (mysql_select_db(&mysql,"kadry"))
die();
if (mysql_query(&mysql,"SELECT * FROM students"))
die();
if (!(res = mysql_store_result(&mysql)))
die();
while((row = mysql_fetch_row(res))) {
for (i = 0 ; i < mysql_num_fields(res); i++)
printf("%s\t",row[i]);
printf ("\n");
}
if (!mysql_eof(res))
die ();
mysql_free_result(res);
mysql_close(&mysql);
}
MySQL API использует структуры данных MYSQL (определены в mysql.h) чтобы установить связь с СУБД. В принципе возможно устанавливать много соединений из одной программы-клиента, но при этом каждое соединение каждое соединений должно быть связано с собственной отдельной структурой struct MYSQL. Которая и объявлена в строке 7.
Строка 8 служит для объявления переменной-ссылки на результат выполнения запроса select.
Строка 9 - объявляет переменную ссылающуюся на одну, конкретную, запись отобранную при переборе в цикле результата выполнения запроса.
Строка 11 содержит объявление функции, выводящей сообщение об ошибке (для этого в строке 12 используется функция mysql_error).
Строка 18 выполняет инициализацию переменной соединения с mysql, и если это не удается, то программа аварийно завершается.
Строка 19 содержит код соединения с СУБД. Обратите внимание на то, что я указал имя машины сервера, а также имя и пароль для доступа к нему. Затем вы задаете номер порта (mysql по-умолчанию исопльзует 3306) далее идут необязательные параметры, которые нас не интересуют.
Строка 21 содержит вызов функции смены текущей базы данных.
Строка 23 содержит запуск запроса отбора информации - всех записей из таблицы students. Для этого используйте mysql_query – данная функция в отличие от одноименной ей в php возвращает не дескриптор результата выполнения а булево-значение – признак успешности операции. Поэтому для доступа к дескриптору результирующего набора строк следует вызвать: mysql_store_result, затем мы печатаем результат выполнения команды, по аналогии с php. Для определения того, сколько было возвращено полей - mysql_num_fields.
Обратите внимание на то что все возвращаемые значения рассматриваюстся как строки текста и переменная типа MYSQL_ROW раскрывается как
typedef char **MYSQL_ROW;
<strong>Строка 32</strong> – необязательная и проверяет что мы действительно пролистали все записи. <strong>Строка 34</strong> – служит для освобождения захваченных ресурсов. <strong>Строка 35</strong> – закрываем соединение с сервером базы данных.Примечание: при работе с большими источниками данных следует учитывать тот момент, что вытягивание на сторону клиента всех записей запроса часто не эффективно, предположим, что вы отображаете результат выборке в сетке вроде ClistCtrl. Пользователь не может обрабатывать выборки более 1000 записей, и использует постраничное листание набора строк или уточняющие запросы (фильтры). Для постраничного отбора следует использовать параметр команды SELECT – LIMIT, например:
SELECT * FROM Students LIMIT 0, 100
Следующим шагом будет использование серверного курсора – концепция серверных курсоров в том, чтобы не вытягивать данные на клиента – а сохранить на стороне сервера виртуальный указатель на текущую запись в результате выборки – затем по мере того как вы с помощью mysql_fetch_row будете переходить по записям нужные записи будет динамически передаваться с сервера к клиенту. Для этого вместо mysql_store_result используйте функцию mysql_use_result. Например, так:
if (!(res = mysql_use_result(&mysql)))
die();
// … и дальше то же что было раньше …
Критически важно: С при использовании mysql_use_result() для инициализации извлечения результирующего набора, клиент получает строки набора с сервера поочередно при повторных вызовах функции mysql_fetch_row(). Поскольку в этом процессе может возникнуть ошибка в соединении, NULL, полученный от mysql_fetch_row(), не всегда означает что мы пролистали все записи. В этом случае вам следует использовать mysql_eof(), чтобы выяснить, что же случилось. mysql_eof() вернет ненулевую величину, если конец результирующего набора был достигнут, и нуль, если произошла ошибка.
mysql_query(&mysql,"SELECT * FROM students");
result = mysql_use_result(&mysql);
while((row = mysql_fetch_row(result))){
// … обработка текущей записи …
}
if(!mysql_eof(result)) { // mysql_fetch_row()
printf("Error: %s\n", mysql_error(&mysql)); // ошибка получения всех записей
}
Исследование результата выборки
Так как все поля выборки возвращаются в виде строк, то важным является опредление того какие типы данных они имеют на самом деле и имена полей, для этого используйте функцию:
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result)
Каждый раз при выполнении нового запроса SELECT функция mysql_fetch_field() сбрасывается в исходное состояние, чтобы возвращать информацию о первом поле. Следовательно если вы последовательно сделали две выборки и пытаетесь узнать информацию о типах полей в первой выборке, то уже поздно.
Содержимое структуры MYSQL_FIELD содержит не только название поля но и еще множестов характеристик:
typedef struct st_mysql_field {
char *name; /* Имя поля */
char *table; /* Имя таблицы из которой было выбрано данное поле */
char *org_table; /* Оригинальное имя таблицы, в предыдущем поле структуры
могло быть указано не настоящее имя таблицы а ее псевдоним */
char *db; /* Имя базы в которой находится та таблица которая содержит текущее поле */
char *def; /* Значение поля по умолчанию */
unsigned long length; /* Размер поля */
unsigned long max_length; /* Максимальная длина поля */
unsigned int flags; /* Флажки для разнообразных признаков */
unsigned int decimals; /* Если поле представляет вещественное число,
то в этом поле хранистся количество знаком после запятой */
enum enum_field_types type; /* Тип поля*/
} MYSQL_FIELD;
enum enum_field_types {
FIELD_TYPE_DECIMAL, FIELD_TYPE_TINY,
FIELD_TYPE_SHORT, FIELD_TYPE_LONG,
FIELD_TYPE_FLOAT, FIELD_TYPE_DOUBLE,
FIELD_TYPE_NULL, FIELD_TYPE_TIMESTAMP,
….И еще много много ….
if (mysql_query(&mysql,"SELECT * FROM students"))
die();
if (!(res = mysql_use_result(&mysql)))
die();
MYSQL_FIELD *field_info;
while((field_info = mysql_fetch_field(res)))
printf("%s(%s)|\t", field_info->name, field_info->org_table);
printf ("\n");
MYSQL_FIELD *mysql_fetch_field_direct(MYSQL_RES *result, unsigned int fieldnr)
Данная функция не является “одноразовой” как предыдущая, и мы можем несколько раз вызвать ее для одного или разных res-указателей на mysql_store_result.
В обоих случаях мы можем узнать параметры (характеристики) поля (так в следующем примере определяется то, является ли поле обязательным для ввода):
if (field->flags & NOT_NULL_FLAG)
printf("Field can't be null\n");
Макрос | Примечание |
IS_NOT_NULL(flags) | Возвращает TRUE, если данное поле определено как NOT NULL |
IS_PRI_KEY(flags) | Возвращает TRUE, если данное поле является первичным ключом |
IS_BLOB(flags) | Возвращает TRUE, если данное поле имеет тип BLOB или TEXT |
Выполнение команд на изменение данных
Для отправки команды DDL или DML используем туже функцию mysql_query, что и при выборке данных.
mysql_query (&mysql , "CREATE aaa TABLE t (i int);");
if (mysql_errno(&mysql)){
printf ("table cannot be created"); die ();
}
else
printf ("table was created");
Таблица. Полный перечень функций mysql:
Функция | Описание |
mysql_affected_rows() | Возвращает количество строк, измененных/удаленных/вставленных последним запросом UPDATE, DELETE или INSERT. |
mysql_change_user() | Переключает пользователя и базу данных для открытого соединения. |
mysql_character_set_name() | Возвращает название кодировки, установленной для данного соединения. |
mysql_close() | Закрывает соединение с сервером. |
mysql_connect() | Создает соединение с сервером баз данных MySQL. Данная функция не рекомендуется; вместо нее следует использовать функцию mysql_real_connect(). |
mysql_create_db() | Создает базу данных. Данная функция не рекомендуется; вместо нее следует использовать команду SQL: CREATE DATABASE. |
mysql_data_seek() | Ищет произвольную строку в результирующем наборе запроса. |
mysql_character_set_name() | Возвращает название кодировки, установленной для данного соединения. |
Функция | Описание |
mysql_drop_db() | Удаляет базу данных. Эта функция не рекомендуется; вместо нее следует использовать команду SQL DROP DATABASE. |
mysql_dump_debug_info() | Заставляет сервер записывать отладочную информацию в журнал. |
mysql_eof() | Определяет, была ли данная строка последней из прочитанных в результирующем наборе данных. |
mysql_errno() | Возвращает номер ошибки для последней запущенной функции MySQL. |
mysql_error() | Возвращает сообщение об ошибке для последней запущенной функции MySQL. |
mysql_drop_db() | Удаляет базу данных. Эта функция не рекомендуется; вместо нее следует использовать команду SQL DROP DATABASE. |
mysql_escape_string() | Экранирует специальные символы в строке, чтобы ее было возможно использовать в команде SQL. |
Функция | Описание |
mysql_fetch_field() | Возвращает тип следующего поля таблицы – помните, что это одноразовая функция. |
mysql_fetch_field_direct() | Возвращает тип поля таблицы по заданному номеру поля. |
mysql_fetch_fields() | Возвращает массив структур, содержащих информацию обо всех полях. |
mysql_fetch_lengths() | Возвращает массив длин всех столбцов в текущей строке. |
mysql_fetch_row() | Извлекает следующую строку из результирующего набора. |
mysql_field_seek() | Устанавливает курсор столбцов на заданный столбец. |
mysql_field_count() | Возвращает количество столбцов в результате для последнего запроса. |
mysql_field_tell() | Возвращает значение положения курсора поля для последнего вызова mysql_fetch_field(). |
mysql_free_result() | Освобождает память, использованную для результирующего набора. |
mysql_get_client_info() | Возвращает информацию о версии клиента. |
mysql_get_host_info() | Возвращает строку, описывающую параметры текущего соединения. |
mysql_get_server_version() | Возвращает номер версии сервера как целое число (новое с 4.1) |
mysql_get_proto_info() | Возвращает версию протокола, используемого для данного соединения. |
mysql_get_server_info() | Возвращает номер версии сервера баз данных. |
mysql_info() | Возвращает информацию о последнем выполненном запросе. |
mysql_init() | Выделяет или инициализирует какую-либо структуру MYSQL. |
mysql_insert_id() | Возвращает идентификатор, сгенерированный для столбца AUTO_INCREMENT предыдущим запросом. |
Пример для SQL-кода:
INSERT INTO foo (auto,text)
VALUES(NULL,'text'); # генерация ID вставкой NULL
INSERT INTO foo2 (id,text)
VALUES(LAST_INSERT_ID(),'text');
# использование ID во второй таблице функция INSERT_ID есть и в SQL и c/c++
if (!mysql_query(&mysql,command))
if (mysql_insert_id (&mysql) == 0)
printf ("table for insert hasnt auto_increment field\n");
else
printf ("table has auto_increment field id = %d\n" , mysql_insert_id (&mysql));
else
printf ("Err: %s" , mysql_error (&mysql));
Функция | Описание |
mysql_kill() | Уничтожает заданный процесс. Все подсоединения к серверу имеют свои номера, получить информацию о которых можно с помощью команды show processlist. В случае необходимости администратор сервера может "убить" подсоединения с помощью команды KILL номер_соединения. |
mysql_list_dbs() | Возвращает имена баз данных, совпадающие с простой строкой шаблона. |
mysql_list_fields() | Возвращает имена полей, совпадающих с простой строкой шаблона. |
mysql_list_processes() | Возвращает список текущих потоков на сервере. |
mysql_list_tables() | Возвращает имена таблиц, совпадающих со строкой шаблона. |
mysql_num_fields() | Возвращает количество столбцов в результирующем наборе. |
mysql_num_rows() | Возвращает количество строк в результирующем наборе. |
Функция | Описание |
mysql_options() | Устанавливает параметры соединения для mysql_connect(). |
mysql_ping() | Проверяет, работает ли данное соединение с сервером, и восстанавливает соединение при необходимости. |
mysql_query() | Выполняет SQL-запрос, заданный в виде строки с нулевым символом в конце. |
Критически важно: Функция mysql_query() не может использоваться для запросов, содержащих двоичные данные; вместо этого необходимо использовать функцию mysql_real_query() (дело в том, двоичные данные могут содержать символ `\0', который mysql_query() воспринимает как окончание строки запроса).
Для проверки, вернул данный запрос результирующий набор или нет, можно использовать функцию mysql_field_count().
Если запрос был выполнен успешно то возвращается нуль иначе код ошибки.
Функция | Описание |
mysql_real_connect() | Создает соединение с сервером баз данных MySQL. Рекомендуемая функция. |
mysql_real_escape_string() | Экранирует специальные символы в строке, чтобы обеспечить возможность использования ее в команде SQL, с учетом установленной для данного соединения кодировки. |
unsigned long mysql_real_escape_string(MYSQL *mysql, char *to, const char *from, unsigned long length)
Пример:
char command [255];
strcpy (command,"INSERT INTO Students (STUD_FIO, BIRTH_DATE, GROUP_ID) VALUES ('");
char * fio = "vasyan-ibn-kozlov's \"junior\" 'tiger' ";
char * nova_fio = new char [1 + 2*strlen (fio)];
mysql_real_escape_string (&mysql , nova_fio , fio , strlen (fio));
strcat (command, nova_fio);
strcat (command, "', '2004-1-1', 131213)");
printf ("Try Command: %s\n", command);
if (!mysql_query(&mysql,command))
printf ("OK: %d %d" , mysql_affected_rows (&mysql), mysql_errno (&mysql));
Функция | Описание |
mysql_real_query() | Выполняет SQL-запрос, заданный в виде фиксированной строки. |
int fh = _open( "C:\\CA_LIC\\pic31_4.bmp", _O_BINARY | _O_RDONLY);
if (fh == -1) abort ();
int len = _filelength( fh ) ;
char * buf = new char [len];
int r = _read (fh , buf , len);
char * command2 = new char [256 + len*2];
char * command2_1 = "UPDATE Students Set PHOTO = '";
char * command2_2 = "' WHERE STUD_ID = 1";
char * pointee = command2;
pointee = strcpy(command2,command2_1);
pointee += mysql_real_escape_string(&mysql, pointee + strlen(pointee) , buf, len);
char * bu = strcat (command2, command2_2);
if (mysql_real_query(&mysql,command2,strlen(command2)))
printf("Failed To Update Row: %s\n", mysql_error(&mysql));
else
printf ("Update OK: %s" , command2);
Функция | Описание |
mysql_reload() | Предписывает серверу перегрузить таблицы привилегий. |
mysql_row_seek() | Устанавливает курсор на заданную строку в результирующем наборе, используя величину, возвращенную из mysql_row_tell(). |
mysql_row_tell() | Возвращает положение курсора строки. |
mysql_select_db() | Выбирает базу данных. |
mysql_shutdown() | Останавливает сервер баз данных. |
mysql_stat() | Возвращает информацию о текущем статусе сервера баз данных в виде строки. |
mysql_store_result() | Извлекает полный результирующий набор для данного клиента. |
mysql_reload() | Предписывает серверу перегрузить таблицы привилегий. |
mysql_thread_id() | Возвращает идентификатор текущего потока. |
mysql_thread_safe() | Возвращает 1, если клиенты скомпилированы как безопасно-поддерживающие потоки. |
mysql_reload() | Предписывает серверу перегрузить таблицы привилегий. |
mysql_use_result() | Инициализирует построчное извлечение результирующего набора. |
« Мелкие хитрости в отладке кода с помощью alert. Часть 1 | ЧПУ (Человеко-понятные УРЛы) в java » |