Статьи

Перенос сайта с MySQL на PostgreSQL — часть 2

В июне 2001 года я переключил внутреннюю базу данных моего сайта с MySQL на PostgreSQL. Это заняло у меня всего один день. С тех пор Postgres работал безупречно, подтверждая, что я сделал правильный выбор.

В первом уроке из этой серии я объяснил, как конвертировать данные из MySQL в Postgres, избегая при этом потенциальных ловушек. Я также объяснил причины моего переезда в Постгрес. В этой статье я проведу вас через другую половину процесса преобразования и объясню, как взять существующий код MySQL PHP и сделать его дружественным Postgres.

подготовка

Прежде чем вы запутаетесь в коде, вам нужно выполнить некоторые подготовительные шаги. Процесс конвертации будет варьироваться в зависимости от ваших навыков кодирования и сложности сайта, но он, безусловно, будет заметен, если вы попытаетесь что-то изменить на живом сайте. Чтобы избежать досадных ошибок при работе на сайте, сделайте копию своего сайта в рабочей области вашего веб-сервера. В моем случае я настроил специальный поддомен для тестирования, пока я модифицировал код. Поскольку сайт автоматически вычисляет URL-адреса на основе некоторых базовых настроек в файле конфигурации, это было легко, но вам, возможно, придется внести некоторые изменения в свой код, чтобы он работал, независимо от того, хотите ли вы разместить версию в процессе разработки в поддомене. в подпапке вашего сайта, на другом веб-хосте или на локальной машине разработки.

Помните: если вы помещаете рабочую копию на другой компьютер, убедитесь, что веб-сервер настроен так же, как действующий сайт, иначе вы можете столкнуться с проблемами. Если ваш веб-хост разрешает соединения Postgres только с действующего веб-сервера, вам, возможно, придется скопировать данные Postgres также на сервер разработки. Для получения дополнительной информации о копировании базы данных Postgres см. Документацию для pg_dumppg_restorewww.postgrsql.org .

После того, как вы скопировали свой сайт и проверили его, чтобы убедиться, что он работает нормально, вы готовы вытащить скальпель и начать взламывать.

PHP хирургия: взлом кода

Сначала создайте центральный файл конфигурации, если у вас его еще нет. Это позволяет легко изменять настройки сервера Postgres в вашем коде и мгновенно влиять на весь ваш сайт. Убедитесь, что файл конфигурации не находится в месте, доступном через Интернет на вашем веб-сервере, что представляет угрозу безопасности. Каталог по умолчанию для PHP — это /usr/local/lib/php/ Также убедитесь, что это не доступно для чтения другим людям, использующим услуги вашего веб-хостинга. Если ваш сайт, как и мой, содержит стандартный заголовочный файл, вы можете включить туда файл настроек Postgres. Это выглядит примерно так:

 <?php  
// /usr/local/lib/php/mysite/configfile.php  
$hostname = "localhost";  
$username = "username";  
$database = "mydb";  
$password = "mypasswd";  
?>  
<?php  
//Standard HTML Header  
include("mysite/configfile.php");  
?>  
 
<html>  
<head>  
<title>Bill's Kazoos</title>  
<head>  
<body>  
header continues...

Центральный репозиторий для информации, как в предыдущем примере, значительно упрощает переключение систем баз данных. Говоря о переключении, пришло время начать редактирование файлов.

Соединения и Запросы

Функции MySQL в PHP достаточно похожи на функции Postgres, чтобы сделать преобразование кода простым, но достаточно разным, чтобы сделать его немного странным. На самом деле, вы можете написать некоторые специальные функции, чтобы упростить конвертацию. Но прежде чем сделать это, вот обзор различий в соединении и запросах:

Подключение к базе данных MySQL требует двух команд:

 $connection_id=mysql_connect($hostname, $username, $password);  
mysql_select_db($database, $connection_id);

Или, если вы используете постоянные соединения:

 $connection_id=mysql_pconnect($hostname, $username, $password);  
mysql_select_db($database, $connection_id);

Однако PHP-функция Postgres connect принимает один строковый аргумент, в отличие от функции MySQL, которая принимает несколько строковых аргументов. Функции Postgres также позволяют указать, какую базу данных использовать при первоначальном подключении. Вот пример:

 $connection_id=pg_connect("host=$hostname dbname=$database user=$username   
password=$password");

Функция постоянного соединения работает точно так же, за исключением того, что она требует вызова pg_pconnect()

Функции запросов MySQL и Postgres в PHP также немного отличаются. Функция запроса MySQL: $result_data = mysql_query("query goes here", $connection_id); , но у Postgres все наоборот: $result_data = pg_exec($connection_id, "query goes here")

Как вы можете видеть, различия между поддержкой MySQL и Postgres в PHP при подключении и запросах невелики, но различия в аргументах функций замедляют процесс редактирования. Чтобы ускорить процесс, вы можете написать некоторые функции-оболочки, которые принимают те же аргументы, что и функции MySQL, но вместо этого подключаются к Postgres. Если у вас есть центральная библиотека утилитарных функций, вы можете разместить их там. Вы также можете поместить их в файл конфигурации, описанный ранее, поскольку он автоматически включается в каждую страницу.

 //connect to database  
function postg_connect($hostname, $username, $password, $database)  
{  
 return pg_connect("host=$hostname dbname=$database  
                    user=$username password=$password");  
}  
 
//alternately, if you use only one database  
//you can make best use of the variables in  
//your configuration file by doing the following,  
//which eliminates the need for any variable  
//passing at all.  
 
function postg_autoconnect()  
{  
 global $hostname, $username $password $database;  
 return pg_connect("host=$hostname dbname=$database  
                    user=$username password=$password");  
}  
 
//query-making function  
function postg_query($query, $connection_id)  
{  
 return pg_exec($connection_id, $query);  
}

Независимо от того, используете ли вы функции-оболочки, преобразование кода соединения и вызовов запросов довольно просто. Postgres в основном может поддерживать старые запросы SQL, используемые в MySQL, но вам, возможно, придется немного подправить свои запросы. Поскольку модели данных и код отличаются от сайта к сайту, я не буду здесь что-либо объяснять. Однако преобразование SQL не так сложно. Сначала преобразуйте код, а затем посмотрите, какие запросы не выполняются в Postgres. Затем сравните справочник по языку MySQL бок о бок с руководством пользователя PostgreSQL . Вероятно, вам не придется делать больше, чем просто находить эквивалент Postgres функций MySQL, поскольку Postgres поддерживает все распространенные функции MySQL.

Теперь, когда вы установили соединение и код запроса для Postgres, пришло время запачкать руки. Различия между поддержкой MySQL и Postgres в PHP создают некоторые препятствия для работы с наборами результатов, что потребует дополнительной настройки кода.

Давайте посмотрим эти результаты

Обработчики набора результатов PHP Postgres могут делать все, что могут их аналоги в MySQL; они просто немного отличаются. Эти небольшие различия могут потребовать лишь небольшого изменения кода, но они также могут потребовать некоторого сложного программирования.

Во-первых, посмотрите, что похоже между MySQL и Postgres. Ниже приведен список общих функций обработки результатов MySQL и их аналоги из Postgres:

MySQL

mysql_num_rows ($ result) Возвращает количество строк в наборе результатов. Это действительно только для ВЫБРАТЬ ЗАЯВЛЕНИЯ

mysql_acted_rows ($ result) Возвращает количество затронутых строк в SQL-запросе INSERT, UPDATE или DELETE.

mysql_fetch_object ($ result) Извлекает одну строку данных и возвращает ее как объект. Имена полей могут быть доступны с использованием синтаксиса класса. (как в $field1 = $var->field1;) Эта функция сохраняет внутреннее значение, чтобы гарантировать, что она возвращает следующую строку при каждом вызове.

mysql_fetch_row ($ result) Эта функция возвращает строку набора результатов в виде числового массива. Доступ к значениям можно получить с помощью синтаксиса массива, начиная с 0. (как в $field1 = $var[0]). Внутренний счетчик отслеживает строки и передает новый результат строки каждый раз, когда он вызывается.

mysql_fetch_array ($ result) Эта функция идентична двум другим функциям извлечения, за исключением того, что она возвращает ассоциативный массив с результатами строк ( $field1 = $var["field1"];).

Postgres

pg_numrows ($ result) работает точно так же, как его аналог MySQL

pg_cmdtuples ($ result) Работает так же, как его аналог MySQL

pg_fetch_object ($ result, $ row) Извлекает обозначенную строку набора результатов. Значение $rowдолжно быть включено, и нет внутреннего счетчика. В противном случае он работает так же, как соответствующая функция MySQL.

pg_fetch_row ($ result, $ row) Возвращает числовой массив результата из указанной строки. Номер строки <b> должен </ b> быть включен. В отличие от аналога MySQL, внутренний счетчик не отслеживает результаты строк.

pg_fetch_array ($ result, $ row) Идентичен его эквиваленту в MySQL, за исключением необходимости указания возвращаемой строки и отсутствия внутреннего счетчика.

Для получения дополнительной информации о функциях PHP MySQL и Postgres см. Документацию PHP на PHP.Net .

Самая большая разница между поддержкой MySQL и Postgres в PHP заключается в фактическом чтении из наборов результатов. Если MySQL автоматически определяет, из какой строки следует возвращать результаты, Postgres требует от вас указать строку, из которой вы хотите прочитать. Это имеет тенденцию мешать многим распространенным алгоритмам чтения данных MySQL. Вот несколько коротких примеров проблемы, с которой вы можете столкнуться, а также два способа ее решения.

 //first common example:   
$rslt=mysql_query("SELECT * FROM blah", $connection_id);  
 
while($value=mysql_fetch_array($rslt))  
{  
 //do data handling stuff  
}  
 
//In the Postgres functions, the previous algo won't work,    
//since they require one to specify row number. The fix  
//goes like this (if you're not using the wrapper function  
//explained earlier):  
 
$rslt=pg_exec($connection_id, "SELECT * from blah");  
$limit=pg_numrows($rslt);  
for($rownum=0;$rownum<$limit;$rownum++)  
{  
 $value=pg_fetch_array($rslt, $rownum);  
 //do stuff here  
}

В предыдущем примере обратите внимание, что для Postgres код немного длиннее, поскольку требуется номер строки. Однако все становится намного проще, если вы напишите свою собственную функцию-оболочку для подсчета и используете ее вместе с функциями-оболочками, показанными ранее. Вот файл утилиты с добавленными функциями оболочки. Обратите внимание на изменения в postg_query()

    
<?php  
// /usr/local/lib/php/mysite/configfile.php  
$hostname = "localhost";  
$username = "username";  
$database = "mydb";  
$password = "mypasswd";  
 
//internal counting variables  
$fetch_array_counter=0;  
$fetch_object_counter=0;  
$fetch_row_counter=0;  
 
//wrapper functions to ease  
//transition to PostgreSQL  
 
//connect to database  
function postg_connect($hostname, $username, $password, $database)  
{  
 return pg_connect(host=$hostname, dbname=$database user=$username,    
password=$password");  
}  
//connect without having to pass values  
function postg_autoconnect()  
{  
 global $hostname, $username, $password, $database;  
 return pg_connect(host=$hostname, dbname=$database user=$username,    
password=$password");  
}  
 
//query-making function  
function postg_query($query, $connection_id)  
{  
 //set the globals to 0  
 global $fetch_array_counter, $fetch_row_counter, $fetch_object_counter;  
 $fetch_array_counter=$fetch_row_counter=$fetch_object_counter=0;  
 return pg_exec($connection_id, $query);  
}  
 
//pg_fetch_array() replacement  
function postg_fetch_array($rslt)  
{  
 global $fetch_array_counter;  
 $fetch_array_counter++; //add one to the counter  
 return pg_fetch_array($rslt, $fetch_array_counter);  
}  
 
//pg_fetch_row() replacement  
function postg_fetch_row($rslt)  
{  
 global $fetch_row_counter;  
 $fetch_row_counter++; //add one to the counter  
 return pg_fetch_row($rslt, $fetch_row_counter);  
}  
 
//pg_fetch_object() replacement  
function postg_fetch_object($rslt)  
{  
 global $fetch_object_counter;  
 $fetch_object_counter++; //add one to the counter  
 return pg_fetch_object($rslt, $fetch_object_counter);  
}  
?>

Конечно, предыдущие функции не будут работать, если вы хотите работать с двумя наборами результатов в одном цикле , так как для каждого типа выборки существует только один внутренний счетчик. Если по какой-то причине вам нужно читать из нескольких наборов результатов взаимозаменяемо, вам придется использовать традиционный метод Postgres.

Другая проблема, с которой вы можете столкнуться при изменении кода, — отсутствие Postgres-эквивалента MySQL mysql_insert_id()INSERT Язык документации PHP может ввести в заблуждение тот, кто считает, что pg_getlastoid() Отсутствие такой функции на самом деле не является недостатком, так как это результат силы Postgres в разрешении нескольких автоинкрементных полей через систему SEQUENCE

К счастью, получить последний идентификатор очень просто. Доступ к информации о последовательности можно получить через SQL, поэтому возможна следующая замена mysql_insert_id()

 function postg_insert_id($tablename, $fieldname)   
{  
 global connection_id;  
 $result=pg_exec($connection_id, "SELECT last_value FROM ${tablename}_  
${fieldname}_seq");  
 $seq_array=pg_fetch_row($result, 0);  
 return $seq_array[0];  
}

Поскольку Postgres использует специальную систему именования для имен последовательностей, функции, которую я создал выше, требуются имя таблицы и имя поля. При вызове вышеприведенная функция извлечет последнее значение последовательности, используемое для любых полей SERIAL

Используя предыдущие методы, вы сможете успешно запустить ваш сайт MySQL с PostgreSQL. Однако это только первый шаг; читайте дальше, чтобы увидеть список полезных ресурсов PostgreSQL.

Ресурсы для дальнейшего чтения

Первый и главный ресурс PostgreSQL можно найти на сайте документации PostgreSQL, не относящемся к FAQ . Этот ценный ресурс содержит обширный список книг, кратких справок, технических руководств и даже рабочих мест. Он также содержит список ресурсов по изменению внутренних баз данных с MySQL на Postgres, а также справку по устранению неполадок и информацию обо всем PostgreSQL.

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

Xach Beane, известный своей работой над GIMP , также написал скрипт для преобразования дампов MySQL в дампы Postgres. Его сценарий обрабатывает несколько вещей, которые не описаны в моей серии статей, так что проверьте это. Конечно, он не обрабатывает вещи без нареканий, поэтому вам, возможно, придется использовать его осторожно.

Добрица Павлинусич также написал скрипт, который обрабатывает преобразование MySQL в Postgres. Еще раз обратите внимание, что источник заявляет о своей неспособности обрабатывать некоторые вещи, поэтому вам, возможно, придется просто прибегнуть к ручному редактированию, как описано в моей предыдущей статье.

Очень подробное руководство по PostgreSQL и PHP можно найти здесь . Он начинается с установки и продолжается до самого начала, чтобы объяснить, как использовать PostgreSQL. Это руководство должно быть обязательно для начинающих.

Брюс Момохан написал книгу о PostgreSQL под названием PostgreSQL: введение и концепции , опубликованную Addison Wesley. Вы даже можете посмотреть его онлайн !

Наконец, OpenDocs публикует Практический PostgreSQL совместно с O’Reilly . Книга должна выйти в начале октября 2001 года, но вы можете прочитать ее на сайте OpenDocs Linuxports.com .

Перемещение серверной части вашего сайта с MySQL на PostgreSQL — это разумный выбор. Преобразование вашего сайта требует времени и самоотдачи, но, выполнив надлежащие шаги, ваш сайт может опираться на более надежную систему баз данных с множеством дополнительных возможностей для дальнейшего расширения. Как и я, вы не пожалеете об этом!

Удачного кодирования!