Приложение «Hello MySQL»
Дата: 27.02.2010
Автор: Павел Пушкарев , paulus (at) sqlinfo (dot) ru
Изучение каждого языка программирования по традиции начинается с примера,
выводящего на экран фразу «Hello world». Эта статья посвящена практически
той же цели — показать, как можно сделать простейшую выборку из MySQL с
использованием разных популярных языков программирования.
Сразу хочу сделать оговорки по поводу того, что будет в примерах, а чего
не будет, и почему я пишу именно так, а не иначе:
- я буду стараться делать максимально короткие примеры, чтобы не
захламлять код ненужным мусором;
- тем не менее, я буду добавлять много комментариев, чтобы код был
читаемым;
- все исключения, которые могут возникнуть в процессе выполнения
программ, я буду обрабатывать, даже если можно написать более
короткий код, просто игнорируя их;
- наверняка, многие программисты смогут написать лучше или лаконичнее,
но моя задача состоит в том, чтобы показать, что код пишется просто
на любом языке — было бы желание. Тем более, что TIMTOWTDI.
Во всех примерах будет решаться одна и та же простая задачка —
вывести строки таблички mysql.user, соответствующие определенному
имени пользователя, передаваемому приложению из командной строки.
Из-за того, что имя передается пользователем, во всех примерах
используется специальный код для того, чтобы избежать инъекций SQL.
В этой статье я рассмотрю три интерпретируемых языка: PHP,
Perl и Python, а также три
компилируемых языка: Java, C и C++.
Hello MySQL с использованием PHP
PHP — один из наиболее популярных сейчас языков, который поддерживается
большинством веб-серверов. Для этого примера, однако, я буду использовать
не серверную, а консольную версию PHP (для того, чтобы можно было передать
параметр из командной строки).
В PHP используют или обычную библиотеку доступа к MySQL или mysqli.
Я буду использовать вторую, так как она общается с MySQL по новому протоколу
и позволяет, например, получать результаты из хранимых процедур. Также она
позволяет использовать объектно-ориентированный интерфейс, который мне близок
по духу.
<?php
// Проверить, есть ли аргумент приложения
if ($argc != 2) die ("USAGE: $argv[0] <username>\n");
// Подключиться к базе
$mysqli = new mysqli("localhost", "root", "", "mysql");
if ($mysqli->connect_error)
die ($mysqli->connect_error . "\n");
// Подготовить запрос
$user = $mysqli->real_escape_string($argv[1]);
if (!($result = $mysqli->query("SELECT * FROM user WHERE user = '$user'")))
die ($mysqli->error . "\n");
// Вывести данные
while ($row = $result->fetch_row()) {
print(join("\t", $row) . "\n");
}
?>
Надо сделать несколько замечаний по использованию этого кода. Во-первых, здесь
я использую real_escape_string, который в обычном коде с mysqli не используется.
В mysqli есть замечательный метод prepare(), с помощью которого можно подготовить
выражение и автоматически экранировать пользовательский ввод:
$stmt = $mysqli->prepare("SELECT * FROM user WHERE user = ?");
$stmt->bind_param("s", $argv[0]);
К сожалению, этот способ не подходит в данном случае, потому что результирующее
подготовленное выражение может отдавать результат только в привязанные переменные
(в отличие от использованного нами $result, позволяющего вытаскивать строки массивом),
то есть мы должны заранее знать список столбцов и привязать к нему наши переменные
PHP. В случае с нашим большим количеством столбцов это не удобно.
Во-вторых, надо понимать, что $mysqli->connect_error содержала ошибку до версии PHP
5.2.9, поэтому следует использовать ее с осторожностью.
Hello MySQL с использованием Perl
Perl — замечательный язык для быстрой обработки текстовой информации. Для доступа к
базам данных Perl использует обобщенный интерфейс DBI.
#! /usr/bin/perl
use strict;
use DBI;
# Проверить, что запустили с параметром
die ("USAGE: $0 <username>\n") unless (@ARGV == 1);
# Отлавливать ошибки в конце
eval {
# Соединиться с базой, включить режим выхода при ошибках
my $dbh = DBI->connect("DBI:mysql:host=localhost;database=mysql", "root", "",
{ PrintError => 0, RaiseError => 1 });
# Выполнить запрос
my $sth = $dbh->selectall_arrayref("SELECT * FROM user WHERE user=?", undef, $ARGV[0]);
# Вывести данные
print (join("\t", @$_) . "\n") foreach (@$sth);
};
# Если случилась ошибка, показать ее
die ("Got error: $@") if ($@);
В отличие от PHP, Perl поддерживает полноценные исключения, которые значительно
упрощают написание части кода, отвечающей за обработку ошибок. Ну и, конечно,
интерфейс DBI позволяет достать данные в том числе и массивом, при этом не
ограничивая использование автоматического экранирования аргументов запроса.
Hello MySQL с использованием Python
Python — современный быстрый язык сценариев, который имеет свою библиотеку
для доступа к MySQL. Так же, как и Perl, он поддерживает исключения, и
поэтому так же, как и в Perl, код его короток и читаем:
#! /usr/bin/env python
# encoding: utf8
import sys
import MySQLdb
# Проверить, что запущены с параметром
if len(sys.argv) != 2:
print("USAGE: " + sys.argv[0] + " <username>")
sys.exit(-1)
# Обрабатывать ошибки в конце
try:
# Подключиться к базе данных
conn = MySQLdb.connect("localhost", "root", "", "mysql")
# Выполнить запрос
cursor = conn.cursor()
cursor.execute("SELECT * FROM user WHERE user = %s", (sys.argv[1]))
rows = cursor.fetchall()
# Вывести результат
for row in rows:
print("\t".join([str(col) for col in row]))
except MySQLdb.Error, e:
print("Got error: %s" % (e.args[1]))
sys.exit(-1)
Hello MySQL с использованием Java
Java — полноценный кроссплатформенный компилируемый язык, который поддерживает
общение с MySQL через обобщенный интерфейс доступа к БД JDBC. Для того, чтобы
работал пример, необходимо, чтобы на клиентском компьютере было установлен
Connector/J.
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class Dump {
public static void main (String[] args) {
// Проверить, что программа запущена с аргументом
if (args.length < 1) {
System.out.println("USAGE: java Dumper <username>");
System.exit(-1);
}
// Проверять ошибки в конце
try {
// Подключиться к базе
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/mysql?user=root");
// Подготовить и выполнить запрос
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM user WHERE user=?");
stmt.setString(1, args[0]);
ResultSet rs = stmt.executeQuery();
// Вывести данные
int columns = rs.getMetaData().getColumnCount();
while (rs.next()) {
for (int i = 1; i <= columns; ++i) {
System.out.print(rs.getString(i) + "\t");
}
System.out.println("");
}
} catch (Exception ex) {
System.out.println("Got error: " + ex.getMessage());
}
}
}
Для того, чтобы это приложение заработало, нужно его правильно скомпилировать
и правильно запустить. С компиляцией всё происходит прямолинейно: достаточно
выполнить команду javac Dumper.java, чтобы получить скомпилированный байткод
приложения Dumper.class. Запуск же этого байткода требует явного указания
пути к драйверу JDBC MySQL:
java -cp /usr/share/java/mysql-connector-java.jar:. Dump
Hello MySQL с использованием С
Язык С — один из самых первых компилируемых языков, который сохранился
до наших дней. В нем нет поддержки исключений,
поэтому, так же, как и на PHP, приходится каждый вызов оборачивать в
if().
Следующий код любезно предоставлен для этой статьи Даниилом Каменским, dkamenskiy (at) yandex (dot) ru.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h>
MYSQL mysql;
/* Функция выводит ошибку MySQL */
void print_error(const char* str)
{
fprintf(stderr, "ERROR: %s\n", str ? str: mysql_error(&mysql));
}
/* Основная функция */
int main (int argc, char** argv)
{
MYSQL_RES *mysqlres;
MYSQL_ROW row;
char *query;
char *tablename;
unsigned rows;
unsigned i;
/* Проверить, что программу запустили с параметром */
if (argc != 2) {
fprintf (stderr, "USAGE: %s username\n", argv[0]);
return -1;
}
/* Инициализировать библиотеку mysql */
if (!mysql_init(&mysql)) {
print_error("Could not initialize MySQL library");
return -1;
}
/* Подключиться к MySQL с параметрами пользователя по умолчанию */
if (!mysql_real_connect(&mysql, "localhost", "root", "", "mysql", 0, NULL, 0)) {
print_error(NULL);
return -1;
}
/* Обработать параметр, чтобы не допустить SQL injection */
tablename = malloc(strlen(argv[1]) * 2 + 1);
mysql_real_escape_string(&mysql, tablename, argv[1], strlen(argv[1]));
/* Составить запрос */
query = strdup ("SELECT * FROM mysql.user WHERE user = '");
query = realloc(query, strlen(query) + strlen(tablename) + 2);
strcat(query, tablename);
strcat(query, "'");
/* Выполнить составленный запрос */
if (mysql_query(&mysql, query)) {
/* Освободить выделенную под составление запроса память */
free(query);
free(tablename);
print_error(NULL);
mysql_close(&mysql);
return -1;
}
/* Освободить выделенную под составление запроса память */
free(query);
free(tablename);
/* Прочитать результат запроса */
if (!(mysqlres = mysql_store_result(&mysql))) {
print_error(NULL);
mysql_close(&mysql);
return -1;
}
/* Вывести результат запроса на экран */
rows = mysql_num_fields(mysqlres);
while ((row = mysql_fetch_row(mysqlres))) {
for (i = 0; i < rows; ++i)
printf("%s\t", row[i]);
printf("\n");
}
/* Освободить результат запроса */
mysql_free_result(mysqlres);
/* Отключиться от MySQL */
mysql_close(&mysql);
return 0;
}
Написание программ на С — развлечение для настоящих программистов :) Нужно
следить абсолютно за всем — начиная от проверок ошибок и заканчивая
корректным освобождением объектов и закрытием всех соединений.
При написании программ на С можно или поступать так, как написано в этом примере,
или эмулировать try-catch блоки с помощью оператора goto. Обычно этот оператор
не рекомендуется использовать, но в данном случае он может значительно сократить
объем кода, а главное улучшить его читаемость.
Для того, чтобы сделать из текста этого примера приложение, нужно его
скомпилировать. Например, если Вы пользуетесь компилятором gcc, то нужно выполнить
gcc -o dumpc main.c -lmysqlclient
Hello MySQL с использованием С++
Многие считают, что С++ — это расширенная версия С. По синтаксису — это почти так.
Но у С++ совсем другая идеология, которую я постараюсь передать.
До недавнего времени,
единственной хорошей библиотекой доступа была Connector/C++, которая работала
полностью подражая JDBC. Следующий пример использует для своей работы, однако,
библиотеку mysql++, которая появилась
относительно недавно, но написана непосредственно для С++, а потому куда удобнее.
#include <iostream>
#include <mysql++.h>
int main (int argc, char* argv[])
{
using namespace mysqlpp;
// Проверить, что программу запустили с параметром
if (argc != 2) {
std::cerr << "USAGE: " << argv[0] << " <username>" << std::endl;
return -1;
}
// Обрабатывать все ошибки в конце
try {
// Подключиться к MySQL
Connection conn("mysql", "localhost", "root", "");
// Составить запрос
Query query = conn.query();
query << "SELECT * FROM mysql.user WHERE user = " << quote << argv[1];
// Выполнить запрос
StoreQueryResult res = query.store();
// Вывести результат
for (size_t row = 0; row < res.num_rows(); ++row) {
for (size_t col = 0; col < res.num_fields(); ++col)
std::cout << res[row][col] << "\t";
std::cout << std::endl;
}
} catch (const std::exception& x) {
std::cerr << "Got error: " << x.what() << std::endl;
}
}
Для того, чтобы скомпилировать это приложение, я использую следующую команду:
g++ -o dumpp main.cc -lmysqlpp
Заключение
Неужели досюда кто-то дочитал? :) В награду Вам ссылка на архив с исходными кодами всех описанных приложений. Желаю удачи в программировании!
Дата публикации: 27.02.2010
© Все права на данную статью принадлежат порталу SQLInfo.ru. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в бумажных изданиях допускается только с разрешения редакции.
|