SQLinfo.ru - Все о MySQL

Приложение «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. Перепечатка в интернет-изданиях разрешается только с указанием автора и прямой ссылки на оригинальную статью. Перепечатка в бумажных изданиях допускается только с разрешения редакции.

Статьи :
 Установка и настройка MySQL
 Коды ошибок в MySQL
>Программирование в MySQL
 Оптимизация производительности
 Кодировка символов в MySQL
 Хранение данных в MySQL
 MySQL Cluster
См. также:
 Оптимизация производительности MySQL
 Услуги по оптимизации MySQL