GrabDuck

Приложение «Hello MySQL» - статьи sqlinfo.ru

:

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