Искать любое упоминание чего-либо в проекте, исключая системные svn файлы:
grep -Rn 'mysql.php' ./ | grep -v svn
Перенести изменения из одного проекта в другой методом патча:
project1 $ svn diff > filename.patch
project2 $ patch -p0 -i ../project1/filename.patch
Собрать файлы в архив по определенному принципу, в данном случае измененные и добавленные файлы:
perl -e '$res = `svn stat|grep -E "^[AM]"`; $res =~ s/\+|^\w+\s*//gm; $res =~ s/\n/ /g; `tar cfz 1.tgz $res`;'
В буду дальнейшем буду пополнять, есть что то вспомню.
пятница, мая 08, 2009
четверг, мая 07, 2009
Google I/O
Как многим разработчикам на Android или просто интерисующимся уже известно, что в 27-28 мая в Сан-франциско пройдет конференция посвященная разработке под Google.Android.
Будет затронуто очень много интересных тем, но самой интересной для меня будет тема посвященная разработке игр.
А именно Chris Pruett расскажет о Writing Real-Time Games for Android, т.к. я сейчас сам разрабатываю игру, о которой скоро расскажу.
Он болжен будет рассказать о фреймворке, который он разрабатывает специально для разработчиков игр. К тому же расскажет о новых возможностях работы с OpenGLES появившихся в AndroidSDK1.5
Немного поискав я нашел (svn) что-то, что разрабатывает Chris, но не уверен, что это именно тот фреймворк, о котором шла речь.
Если кто "в теме" и нашел этот фреймворк, буду признателен, если скинете ссылку.
Будет затронуто очень много интересных тем, но самой интересной для меня будет тема посвященная разработке игр.
А именно Chris Pruett расскажет о Writing Real-Time Games for Android, т.к. я сейчас сам разрабатываю игру, о которой скоро расскажу.
Он болжен будет рассказать о фреймворке, который он разрабатывает специально для разработчиков игр. К тому же расскажет о новых возможностях работы с OpenGLES появившихся в AndroidSDK1.5
Немного поискав я нашел (svn) что-то, что разрабатывает Chris, но не уверен, что это именно тот фреймворк, о котором шла речь.
Если кто "в теме" и нашел этот фреймворк, буду признателен, если скинете ссылку.
вторник, мая 05, 2009
Samsung I7500 будет первым телефоном на Android в России
Пресс-центр корпорации Samsung объявил о примерной дате выхода смартфона на базе Android: 3 квартал 2009 года.
Возникает много вопросов таких как:
Т.к. в России Samsung пользуется большой популярностью, то ответ на 3 вопрос выглядит логичным.
Если рассуждать дальше, то Google согласен пустить первым Samsung.
К тому же если посмотреть темпы распространения платформы по странам(в прошивке 1.5 уже доступна русская локаль), то скорее всего Android.Market в России в тому времени будет.
А стоить мне кажется он будет не дешевле 20 тыс. руб., чтобы составить конкуренцию iPhone.
Возникает много вопросов таких как:
- Будет ли Android.Market в России?
- Сколько будет стоить устройство?
- Почему Google позволяет появится первым устройством в России не свой аппарат G1 или G2?
Т.к. в России Samsung пользуется большой популярностью, то ответ на 3 вопрос выглядит логичным.
Если рассуждать дальше, то Google согласен пустить первым Samsung.
К тому же если посмотреть темпы распространения платформы по странам(в прошивке 1.5 уже доступна русская локаль), то скорее всего Android.Market в России в тому времени будет.
А стоить мне кажется он будет не дешевле 20 тыс. руб., чтобы составить конкуренцию iPhone.
суббота, марта 28, 2009
Segmentation fault в php5
Взгляните на этот код:
С первого взгляда все верно. Но выдает:
[Sat Mar 28 17:12:59 2009] [notice] child pid 11239 exit signal Segmentation fault (11)
Образуется цикл, тогда надо:
class A extends B
{
public function getA(){
if($this->getA()){
return $this->getA();
} else {
return $this->getB();
}
}
}
С первого взгляда все верно. Но выдает:
[Sat Mar 28 17:12:59 2009] [notice] child pid 11239 exit signal Segmentation fault (11)
Образуется цикл, тогда надо:
class A extends B
{
public function getA(){
if(parent::getA()){
return parent::getA();
} else {
return $this->getB();
}
}
}
среда, марта 25, 2009
Монтирование(mount) файловой системы по ssh
sudo apt-get install sshfs
sudo sshfs -o allow_other username@hostname.ru:/home/usernamedir /home/usermountdir
Можно создать файл mount.sh и сделать ярлык для запуска на него. Gnome легко определяет что надо ввести пароль для ssh и предлагает сделать это через GUI
sudo sshfs -o allow_other username@hostname.ru:/home/usernamedir /home/usermountdir
Можно создать файл mount.sh и сделать ярлык для запуска на него. Gnome легко определяет что надо ввести пароль для ssh и предлагает сделать это через GUI
вторник, марта 24, 2009
Необходимые компоненты для работы symfony в Ubuntu
Недавно переставил систему с ubuntu 8.10 x86 на ubuntu 9.04 x86_64 на свой рабочий acer aspire 5720G.
Пришлось настраивать систему заново, но так как я сейчас разрабатываю только под symfony. Настраивал работу системы как раз для симфони, поэтому можно рассматривать этот пост как Необходимые компоненты для работы symfony в Ubuntu.
Поставим apache и php5.
sudo apt-get install apache2 php5-mysql libapache2-mod-php5 php5-common mysql-server mysql-common mysql-client-5.0 php5-cli php5-xsl
Устанавливаем кодировку по умолчанию для mysql:
sudo mcedit /etc/mysql/my.cnf
или
sudo nano /etc/mysql/my.cnf
добавить в конец строчки:
вместо utf8 можно любую другую, например cp1251
Ставим svn. Если не используете, то можно пропустить.
Ставим symfony:
sudo mkdir -p /usr/share/php5/symfony-1.2/
sudo svn co http://svn.symfony-project.com/branches/1.2 /usr/share/php5/symfony-1.2/
Если проект начинаете с нуля:
Подключаем библиотеку symfony. В файле:
Проверяем
cp /var/www/symfony/
./symfony
если все без ошибок, то можно приступать к работе
Пришлось настраивать систему заново, но так как я сейчас разрабатываю только под symfony. Настраивал работу системы как раз для симфони, поэтому можно рассматривать этот пост как Необходимые компоненты для работы symfony в Ubuntu.
Поставим apache и php5.
sudo apt-get install apache2 php5-mysql libapache2-mod-php5 php5-common mysql-server mysql-common mysql-client-5.0 php5-cli php5-xsl
Устанавливаем кодировку по умолчанию для mysql:
sudo mcedit /etc/mysql/my.cnf
или
sudo nano /etc/mysql/my.cnf
добавить в конец строчки:
default-character-set=utf8
character_set_client=utf8
вместо utf8 можно любую другую, например cp1251
Ставим svn. Если не используете, то можно пропустить.
apt-get install subversion
libapache2-svn ssl-cert libapache2-svn - опционально
Ставим symfony:
sudo mkdir -p /usr/share/php5/symfony-1.2/
sudo svn co http://svn.symfony-project.com/branches/1.2 /usr/share/php5/symfony-1.2/
<VirtualHost 127.0.0.1:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/symfony/web/
<Directory />
Options FollowSymLinks Indexes
AllowOverride All
</Directory>
ErrorLog /var/log/apache2/error.log
LogLevel warn
CustomLog /var/log/apache2/access.log combined
</VirtualHost>
Если проект начинаете с нуля:
wget http://www.symfony-project.org/get/sf_sandbox_1_2.tgz
tar xfz sf_sandbox_1_2.tgz
cp sf_sandbox
mv sf_sandbox/* /var/www/symfony/
Подключаем библиотеку symfony. В файле:
/var/www/symfony/config/ProjectConfiguration.class.php
меняем на:
require_once '/usr/share/php5/symfony-1.2/lib/autoload/sfCoreAutoload.class.php';
Проверяем
cp /var/www/symfony/
./symfony
если все без ошибок, то можно приступать к работе
понедельник, марта 23, 2009
Группировка виджетов(sfWidget) в админ генераторе(admin-generator) для symfony 1.2
Сегодня мне понадобилось динамически стороить элементы формы в админ генераторе и группировать их.
Это оказалось не столь простой задачей.
Я использую symfony 1.2.5-DEV из svn.
Сразу оговорюсь, что строить из embedded forms я не хочу, т.к. элементы динамические.
Вот мои действия. Постоим простейший вариант:
сгенерированный html код будет:

Смотрится - не очень. Тогда покопавшись в коде я попробовал указать FormFormatter:
Эффект тот же.
Тогда я использовал RowFormat:
сгенерированный html код будет:

Тоже не то что я ожидал.
И выходом из всего этого будет:
сгенерированный html код будет:

Это и есть то что нам надо.
Похоже на то, что setFormFormatterName работает только с embedded forms.
Я пытался попросить объяснений это на гугл групс, но без результатно.
Теперь самое интересное. Как же будет выглядеть валидатор:
Таким образом используя sfWidgetFormSchema можно делать сгруппированные формы любой вложенности.
Это оказалось не столь простой задачей.
Я использую symfony 1.2.5-DEV из svn.
Сразу оговорюсь, что строить из embedded forms я не хочу, т.к. элементы динамические.
Вот мои действия. Постоим простейший вариант:
$this->widgetSchema['group0'] = new sfWidgetFormSchema(array(
'name0' => new sfWidgetFormInput(),
'email0' => new sfWidgetFormInput(),
));
сгенерированный html код будет:
<div>
<label for="t_object_key_group0">Group0</label>
<label for="t_object_key_group0_name0">Name0</label>
<input name="t_object_key[group0][name0]" id="t_object_key_group0_name0" type="text">
<label for="t_object_key_group0_email0">Email0</label>
<input name="t_object_key[group0][email0]"id="t_object_key_group0_email0" type="text">
</div>
Смотрится - не очень. Тогда покопавшись в коде я попробовал указать FormFormatter:
$decorator = new sfWidgetFormSchemaFormatterTable($this->widgetSchema
['group0']);
$this->widgetSchema['group0']->addFormFormatter('custom', $decorator);
$this->widgetSchema['group0']->setFormFormatterName('custom');
Эффект тот же.
Тогда я использовал RowFormat:
$this->widgetSchema['group0']->getFormFormatter()->setRowFormat
("\n<table> <th>%label%</th>\n <td>%error%%field%%help%
%hidden_fields
%</td>\n</tr></table>");
сгенерированный html код будет:
<div>
<label for="t_object_key_group0">Group0</label>
<table> <tbody><tr><th><label for="t_object_key_group0_name0"
>Name0</label></th>
<td><input name="t_object_key[group0][name0]"id="t_object_key_group0_name0" type="text"></td>
</tr></tbody></table>
<table> <tbody><tr><th><label
for="t_object_key_group0_email0">Email0</label></th>
<td><input name="t_object_key[group0][email0]" id="t_object_key_group0_email0" type="text"></td>
</tr></tbody></table>
</div>
Тоже не то что я ожидал.
И выходом из всего этого будет:
$this->widgetSchema['group0'] = new sfWidgetFormSchema(array(
'grouped' => new sfWidgetFormSchema(array(
'name0' => new sfWidgetFormInput(),
'email0' => new sfWidgetFormInput(),
))
));
$this->widgetSchema['group0']->getFormFormatter()->setRowFormat
("\n<table> <th>%label%</th>\n <td>%error%%field%%help%
%hidden_fields
%</td>\n</tr></table>");
сгенерированный html код будет:
<div>
<label for="t_object_key_group0">Group0</label>
<table> <tbody><tr><th>Grouped</th>
<td></td></tr><tr>
<th><label for="t_object_key_group0_grouped_name0">Name0</label></th>
<td><input name="t_object_key[group0][grouped][name0]"
id="t_object_key_group0_grouped_name0" type="text"></td>
</tr>
<tr>
<th><label for="t_object_key_group0_grouped_email0">Email0</label></
th>
<td><input name="t_object_key[group0][grouped][email0]"
id="t_object_key_group0_grouped_email0" type="text"></td>
</tr>
</tbody></table>
</div>
Это и есть то что нам надо.
Похоже на то, что setFormFormatterName работает только с embedded forms.
Я пытался попросить объяснений это на гугл групс, но без результатно.
Теперь самое интересное. Как же будет выглядеть валидатор:
$this->validatorSchema['group0'] = new sfValidatorSchema(array(
'grouped' => new sfValidatorSchema(array(
'name0' => new sfValidatorInteger(),
'email0' => new sfValidatorString(),
))
));
Таким образом используя sfWidgetFormSchema можно делать сгруппированные формы любой вложенности.
Интересный момент в mysql 5 для foreign key
CREATE TABLE `key` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`slug` varchar(128) NOT NULL,
`group_id` int(11) NOT NULL,
`parent_id` int(11) default '0',
`comp_id` int(11) default NULL,
PRIMARY KEY (`id`),
KEY `key_FI_1` (`group_id`),
KEY `key_FI_2` (`parent_id`),
CONSTRAINT `key_FK_1` FOREIGN KEY (`group_id`) REFERENCES `key_group` (`id`) ON DELETE CASCADE,
CONSTRAINT `key_FK_2` FOREIGN KEY (`parent_id`) REFERENCES `key` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
С первого взгляда все ок.
Теперь попробуем вставить запись:
insert into key (name,group_id) values ('ключ1',1)выходит ошибка
mysql>ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`database/key`, CONSTRAINT `key_FK_2` FOREIGN KEY (`parent_id`) REFERENCES `key` (`id`) ON DELETE CASCADE)
Дело оказывается в `parent_id` int(11) default '0' , т.к. нет записи с id=0.
Правильнее сделать `parent_id` int(11) default NULL.
Мне кажется в propel при генерации из схемы можно сделать проверку на defaultValue, т.к. в купе с foreign key это уже ошибочно в большинстве случаев.
воскресенье, марта 22, 2009
Правильное расширение базовых классов Propel в symfony 1.2
Пусть наша модель называется SomeModel, тогда методом для извлечения объекта по строке(slug) будет:
class SomeModelPeer extends BaseSomeModelPeer
{
public static function retrieveBySlug($slug, PropelPDO $con = null){
if (null !== ($obj = SomeModelPeer::getInstanceFromPool((string) $slug))) {
return $obj;
}
if ($con === null) {
$con = Propel::getConnection(self::DATABASE_NAME, Propel::CONNECTION_READ);
}
$criteria = new Criteria();
self::addSelectColumns($criteria);
$criteria->add(self::SLUG,$slug);
$object = self::doSelectOne($criteria,$con);
if($object instanceof SomeModel){
self::addInstanceToPool($object);
}
return $object;
}
}
среда, марта 18, 2009
Как динамически изменить layout для всего application
Иногда нужно менять layout для всего приложения, на пример в празничные дни.
Но писать условия в каждом модуле - не красиво.
Поэтому можно сделать это заранее в фильтре.
В файле app/your_module/config/filters.yml добавь ваш фильтр:
Создайте класс changeLayoutFilter и положите его в lib/:
isNY() - это ваше условие, 'your_layout' - ваш layout.
Кстати, почему я не использовал конструкцию по учебнику:
?
Потому что если будет использован forward в контроллере(sfAction), то
цепочка фильтров будет запущено заново и наш хак не сработает.
Посмотрите сами в дебагере, вкладка "log" как ваш фильтр будет запущен 2 раза.
Но писать условия в каждом модуле - не красиво.
Поэтому можно сделать это заранее в фильтре.
В файле app/your_module/config/filters.yml добавь ваш фильтр:
rendering: ~
security: ~
# insert your own filters here
change_layuot:
class: changeLayoutFilter
param:
cache: ~
common: ~
execution: ~
Создайте класс changeLayoutFilter и положите его в lib/:
class changeLayoutFilter extends sfFilter
{
public function execute($filterChain)
{
$request = $this->getContext()->getRequest();
$user = $this->getContext()->getUser();
//устанавливаю layuot
if(isNY()){
sfConfig::set('symfony.view.'.
$this->getContext()->getModuleName().'_'.
$this->getContext()->getActionName().'_layout',
'your_layout');
}
// Execute next filter
$filterChain->execute();
}
}
isNY() - это ваше условие, 'your_layout' - ваш layout.
Кстати, почему я не использовал конструкцию по учебнику:
class changeLayoutFilter extends sfFilter
{
public function execute($filterChain)
{
// Execute this filter only once
if ($this->isFirstCall())
{
[... ...]
}
}
}
?
Потому что если будет использован forward в контроллере(sfAction), то
цепочка фильтров будет запущено заново и наш хак не сработает.
Посмотрите сами в дебагере, вкладка "log" как ваш фильтр будет запущен 2 раза.
вторник, марта 17, 2009
Валидатор телефонного номера в symfony 1.2
Телефонный номер вида: +7(495)1234567
чтобы заработал положить в lib/
/**
* sfValidatorTelephone
*
* @package
* @subpackage validator
* @author broderix
* @version
*/
class sfValidatorTelephone extends sfValidatorRegex
{
/**
* @see sfValidatorRegex
*/
protected function configure($options = array(), $messages = array())
{
parent::configure($options, $messages);
$this->setOption('pattern', '/^(\+\d\(\d+\)\d*)$/i');
}
}
чтобы заработал положить в lib/
суббота, марта 14, 2009
Упрощение работы с sfWidgetFormSelect в sfForm
Этим постом я начну серию статьёй по symfony 1.2
Иногда необходимо использовать ограниенные списки select в админе в symfony 1.2.
Но не хочется выбирать данные для sfWidgetFormSelect из массива или yaml файла.
Поэтому можно создать свой виджет sfWidgetFormYamlSelect:
В файле config/app.yml (если его нет, то создайте) опишите ваши поля для форм:
В самой форме это будет выглядеть так:

Так же это можно использовать в фильтрах.
Иногда необходимо использовать ограниенные списки select в админе в symfony 1.2.
Но не хочется выбирать данные для sfWidgetFormSelect из массива или yaml файла.
Поэтому можно создать свой виджет sfWidgetFormYamlSelect:
class sfWidgetFormYamlSelect extends sfWidgetFormChoice
{
public function __construct($options = array(), $attributes = array())
{
$options['choices'] = new sfCallable(array($this, 'getChoices'));
parent::__construct($options, $attributes);
}
protected function configure($options = array(), $attributes = array())
{
addRequiredOption('form_name', null);
$this->addRequiredOption('field_name', null);
$this->addOption('add_empty', false);
parent::configure($options, $attributes);
}
public function getChoices()
{
$conf_array = sfConfig::get('app_'.$this->getOption('form_name'));
$choices = array();
if(false !== $this->getOption('add_empty')){
$choices[''] = true === $this->getOption('add_empty') ? '' :
$this->getOption('add_empty');
}
$choices = array_merge($choices,$conf_array[$this->getOption('field_name')]);
return $choices;
}
}
В файле config/app.yml (если его нет, то создайте) опишите ваши поля для форм:
all:
.form_settings:
my_form1:
type: { 'yes': 'да', 'no': 'нет }
my_form2:
status: { 0: 'новый', 1: 'подтвержденный', 2: 'редактируется' }
В самой форме это будет выглядеть так:
class MyFormForm1 extends BaseMyForm1Form
{
public function configure()
{
$this->setWidget('status' , new sfWidgetFormYamlSelect(array(
'form_name'=>'my_form1',
'field_name'=>'status'
)));
}
}

Так же это можно использовать в фильтрах.
четверг, января 22, 2009
Обзор шаблонов проектирования
Сегодня наткнулся на хороший обзор шаблонов проектирования. Автор проекта monkeycode.ru периодически выкладывает шаблоны проектирования с краткими пояснениями пока только на языке php5.
Думаю чтобы понять смысл этого достаточно.
Будем следить.
Все шаблоны можно вытащить по тегу шаблоны проектирования.
Думаю чтобы понять смысл этого достаточно.
Будем следить.
Все шаблоны можно вытащить по тегу шаблоны проектирования.
понедельник, июня 18, 2007
Миграция с MySQL 5.X на Oracle 10.X с обратной совместимостью на платформе php
Этая серия статей направлена на помощь при миграция с MySQL 5.X на Oracle 10.X на платформе php для разработчиков, которые еще не сталкивались СУБД Oracle. Она была написана в ходе собственных проб и ошибок при переносе данных в 700 Мб.
Данную серию статей рассматривайте как рекомендации для новичков, естесственно в них есть удачные и не удачные решения. И тем не мение процесс миграции прошел успешно с поддержкой двух СУБД.
Содержание:
Данную серию статей рассматривайте как рекомендации для новичков, естесственно в них есть удачные и не удачные решения. И тем не мение процесс миграции прошел успешно с поддержкой двух СУБД.
Содержание:
воскресенье, июня 17, 2007
Установка и настройка Oracle Client 10.X
Другие статьи:
- Установка и настройка Oracle
- Разработка ПО на php под Oracle 10.X и MySQL
- Перенос данных с Mysql на Oracle
суббота, июня 16, 2007
Разработка ПО на php под Oracle 10.X и MySQL
Даты, время
Используйте CURRENT_TIMESTAMP для совместимости с MySQL вместо NOW(), SYSDATE().
Oracle> select CURRENT_TIMESTAMP from dual;
CURRENT_TIMESTAMP
------------------------------------------
14-JUN-07 02.47.52.987970 PM +04:00
mysql> select CURRENT_TIMESTAMP;
+---------------------+
| CURRENT_TIMESTAMP |
+---------------------+
| 2007-06-14 14:58:23 |
+---------------------+
1 row in set (0.00 sec)
Установите стандартное время для MySQL в формате 'YYYY-MM-DD HH24:MI:SS'
Oracle> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
Session altered.
Старайтесь выкручиваться разными приёмами:
mysql> select hour(timediff(now(), t_timestamp)) from table1;
Oracle> select CEIL(SYSDATE - t_timestamp) from table1;
если неудаётся, то ...
Создайте недостающие функции:
FIND_IN_SET
CREATE OR REPLACE FUNCTION
FIND_IN_SET(TWHAT VARCHAR2, TWHERE VARCHAR2) RETURN INTEGER
IS
BEGIN
DECLARE
CDATA varchar2(4000);
CURSOR TDATA IS
SELECT REGEXP_REPLACE(REGEXP_SUBSTR(TWHERE, ','||TWHAT||',|^'||TWHAT||',|^'||TWHAT||'$|,'||TWHAT||'$'),',','') FROM DUAL;
FROM DUAL;
BEGIN
open TDATA;
FETCH TDATA into CDATA;
close TDATA;
IF (TO_CHAR(CDATA) = TWHAT) THEN
return 1;
ELSE
return 0;
END IF;
END;
END FIND_IN_SET;
/
Тесты:
Oracle> SELECT table1.field1 from table1
WHERE find_in_set('5',table1.field1) >= 1;
table1.field1
-------------
',5,1,2,3,4,'
mysql> SELECT table1.field1 from table1
WHERE find_in_set('5',table1.field1) >= 1;
+---------------+
| table1.field1 |
+---------------+
| ,5,1,2,3,4, |
+---------------+
1 row in set (0.00 sec)
CONCAT_WS
Oracle> CREATE OR REPLACE FUNCTION
CONCAT_WS(PATTERN VARCHAR2,
STR1 VARCHAR2 DEFAULT '',
STR2 VARCHAR2 DEFAULT '',
STR3 VARCHAR2 DEFAULT '',
STR4 VARCHAR2 DEFAULT '',
STR5 VARCHAR2 DEFAULT '',
STR6 VARCHAR2 DEFAULT '',
STR7 VARCHAR2 DEFAULT '',
STR8 VARCHAR2 DEFAULT '',
STR9 VARCHAR2 DEFAULT '',
STR10 VARCHAR2 DEFAULT '',
STR11 VARCHAR2 DEFAULT '',
STR12 VARCHAR2 DEFAULT ''
) RETURN VARCHAR2
IS
BEGIN
DECLARE
CDATA varchar2(4000):='';
BEGIN
IF (LENGTH(STR1) > 0) THEN
CDATA := STR1;
END IF;
IF (LENGTH(STR2) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR2);
END IF;
IF (LENGTH(STR3) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR3);
END IF;
IF (LENGTH(STR4) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR4);
END IF;
IF (LENGTH(STR5) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR5);
END IF;
IF (LENGTH(STR6) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR6);
END IF;
IF (LENGTH(STR7) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR7);
END IF;
IF (LENGTH(STR8) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR8);
END IF;
IF (LENGTH(STR9) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR9);
END IF;
IF (LENGTH(STR10) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR10);
END IF;
IF (LENGTH(STR11) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR11);
END IF;
IF (LENGTH(STR12) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR12);
END IF;
return CDATA;
END;
END;
/
Function created.
Тесты:
Oracle> select concat_ws('::','1','2') from dual;
CONCAT_WS('::','1','2')
------------------------------------------------
1::2
Oracle> select concat_ws('::','1') from dual;
CONCAT_WS('::','1')
------------------------------------------------
1
mysql> select concat_ws('::','1','2');
+-------------------------+
| concat_ws('::','1','2') |
+-------------------------+
| 1::2 |
+-------------------------+
1 row in set (0.00 sec)
mysql> select concat_ws('::','1');
+---------------------+
| concat_ws('::','1') |
+---------------------+
| 1 |
+---------------------+
1 row in set (0.00 sec)
Но не всегда получается сделать универсальный код или создать недостающие функции, слишком проблематично или сложно, учитывая временные рамки. Чтобы выкрутится, переложите часть забот с sql на плечи вашего скриптового языка.
PHP
Если множество решений для разработки приложений поддерживающих множество СУБД. Можно пойти:
1) путем обработки запроса регулярными выражениями подаваемого в php-класс под каждую СУБД(пример в исходных кодах phpbb.com, 3-я версия поддерживает mysql, oracle, postgresql).
Преимущества:
Недостатки
2) путем обработки запроса перед тем как подать его в php-класс.
Преимущества:
Недостатки
Я выбрал 2 способ:
Если запрос сложнее, то необходимо обрабатывать его начиная с блока WHERE:
(заметьте, что в блоке GROUP BY для Oracle приходится перечислять все поля)
Еще пример:
ORA ERRORS
ORA-00923: ключевое слово FROM не найдено там, где оно ожидалось.
возникает, если использовать ключевые слова: number, char, date , ...
в конструкциях:
sql> select somename number, somedate date from sometable;
ORA-00933: неверное завершение SQL-предложения
Возникает, если вы использовали посторонние символы.
Как правило это 'AS', Oracle не понимает эту конструкцию:
Oracle> SELECT table1.field1 AS f FROM table1;
Другие статьи:
Используйте CURRENT_TIMESTAMP для совместимости с MySQL вместо NOW(), SYSDATE().
Oracle> select CURRENT_TIMESTAMP from dual;
CURRENT_TIMESTAMP
------------------------------------------
14-JUN-07 02.47.52.987970 PM +04:00
mysql> select CURRENT_TIMESTAMP;
+---------------------+
| CURRENT_TIMESTAMP |
+---------------------+
| 2007-06-14 14:58:23 |
+---------------------+
1 row in set (0.00 sec)
Установите стандартное время для MySQL в формате 'YYYY-MM-DD HH24:MI:SS'
Oracle> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
Session altered.
Старайтесь выкручиваться разными приёмами:
mysql> select hour(timediff(now(), t_timestamp)) from table1;
Oracle> select CEIL(SYSDATE - t_timestamp) from table1;
если неудаётся, то ...
Создайте недостающие функции:
FIND_IN_SET
CREATE OR REPLACE FUNCTION
FIND_IN_SET(TWHAT VARCHAR2, TWHERE VARCHAR2) RETURN INTEGER
IS
BEGIN
DECLARE
CDATA varchar2(4000);
CURSOR TDATA IS
SELECT REGEXP_REPLACE(REGEXP_SUBSTR(TWHERE, ','||TWHAT||',|^'||TWHAT||',|^'||TWHAT||'$|,'||TWHAT||'$'),',','') FROM DUAL;
FROM DUAL;
BEGIN
open TDATA;
FETCH TDATA into CDATA;
close TDATA;
IF (TO_CHAR(CDATA) = TWHAT) THEN
return 1;
ELSE
return 0;
END IF;
END;
END FIND_IN_SET;
/
Тесты:
Oracle> SELECT table1.field1 from table1
WHERE find_in_set('5',table1.field1) >= 1;
table1.field1
-------------
',5,1,2,3,4,'
mysql> SELECT table1.field1 from table1
WHERE find_in_set('5',table1.field1) >= 1;
+---------------+
| table1.field1 |
+---------------+
| ,5,1,2,3,4, |
+---------------+
1 row in set (0.00 sec)
CONCAT_WS
Oracle> CREATE OR REPLACE FUNCTION
CONCAT_WS(PATTERN VARCHAR2,
STR1 VARCHAR2 DEFAULT '',
STR2 VARCHAR2 DEFAULT '',
STR3 VARCHAR2 DEFAULT '',
STR4 VARCHAR2 DEFAULT '',
STR5 VARCHAR2 DEFAULT '',
STR6 VARCHAR2 DEFAULT '',
STR7 VARCHAR2 DEFAULT '',
STR8 VARCHAR2 DEFAULT '',
STR9 VARCHAR2 DEFAULT '',
STR10 VARCHAR2 DEFAULT '',
STR11 VARCHAR2 DEFAULT '',
STR12 VARCHAR2 DEFAULT ''
) RETURN VARCHAR2
IS
BEGIN
DECLARE
CDATA varchar2(4000):='';
BEGIN
IF (LENGTH(STR1) > 0) THEN
CDATA := STR1;
END IF;
IF (LENGTH(STR2) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR2);
END IF;
IF (LENGTH(STR3) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR3);
END IF;
IF (LENGTH(STR4) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR4);
END IF;
IF (LENGTH(STR5) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR5);
END IF;
IF (LENGTH(STR6) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR6);
END IF;
IF (LENGTH(STR7) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR7);
END IF;
IF (LENGTH(STR8) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR8);
END IF;
IF (LENGTH(STR9) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR9);
END IF;
IF (LENGTH(STR10) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR10);
END IF;
IF (LENGTH(STR11) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR11);
END IF;
IF (LENGTH(STR12) > 0) THEN
CDATA := CONCAT(CDATA,PATTERN);
CDATA := CONCAT(CDATA,STR12);
END IF;
return CDATA;
END;
END;
/
Function created.
Тесты:
Oracle> select concat_ws('::','1','2') from dual;
CONCAT_WS('::','1','2')
------------------------------------------------
1::2
Oracle> select concat_ws('::','1') from dual;
CONCAT_WS('::','1')
------------------------------------------------
1
mysql> select concat_ws('::','1','2');
+-------------------------+
| concat_ws('::','1','2') |
+-------------------------+
| 1::2 |
+-------------------------+
1 row in set (0.00 sec)
mysql> select concat_ws('::','1');
+---------------------+
| concat_ws('::','1') |
+---------------------+
| 1 |
+---------------------+
1 row in set (0.00 sec)
Но не всегда получается сделать универсальный код или создать недостающие функции, слишком проблематично или сложно, учитывая временные рамки. Чтобы выкрутится, переложите часть забот с sql на плечи вашего скриптового языка.
PHP
Если множество решений для разработки приложений поддерживающих множество СУБД. Можно пойти:
1) путем обработки запроса регулярными выражениями подаваемого в php-класс под каждую СУБД(пример в исходных кодах phpbb.com, 3-я версия поддерживает mysql, oracle, postgresql).
Преимущества:
- можно добавить поддержку СУБД на любом этапе разработки;
Недостатки
- сложность в составлении регулярных выражениях, есть вероятность ошибки, т.е. в равно приходится возвращаться к старому коду;
2) путем обработки запроса перед тем как подать его в php-класс.
Преимущества:
- надежность кода, вы всегда знаете как будет выполняться тот или иной запрос;
Недостатки
- для добавления поддержки новой СУБД придется изменять все запросы во всем коде;
Я выбрал 2 способ:
if($config['driver'] == "mysql"){
$db_limit = " LIMIT 1";
} else {
if($['driver'] == "oracle"){
$db_limit = " AND rownum = 1";
}
}
$sql = "
SELECT field1 f1,field2 f2
FROM table1
WHERE f1 = f2 ".$db_limit;
Если запрос сложнее, то необходимо обрабатывать его начиная с блока WHERE:
(заметьте, что в блоке GROUP BY для Oracle приходится перечислять все поля)
if($config['driver'] == "mysql"){
$db_stuff = "
WHERE f1 = f2
GROUP BY field1
ORDER BY field1 DESC
LIMIT 1";
} else {
if($['driver'] == "oracle"){
$db_stuff = "
WHERE f1 = f2 AND rownum = 1
GROUP BY field1,field2
ORDER BY field1 DESC";
}
}
$sql = "
SELECT field1 f1,field2 f2
FROM table1 ".
$db_stuff;
Еще пример:
if($config['driver'] == "mysql"){
$db_hour = "hour(timediff(now(), t_timestamp))";
} else {
if($config['driver'] == "oracle"){
$db_hour = "CEIL(SYSDATE - t_timestamp)";
}
}
$sql = "
SELECT field1 f1,field2 f2, ".
$db_hour." t_hour"
."FROM table1"
ORA ERRORS
ORA-00923: ключевое слово FROM не найдено там, где оно ожидалось.
возникает, если использовать ключевые слова: number, char, date , ...
в конструкциях:
sql> select somename number, somedate date from sometable;
ORA-00933: неверное завершение SQL-предложения
Возникает, если вы использовали посторонние символы.
Как правило это 'AS', Oracle не понимает эту конструкцию:
Oracle> SELECT table1.field1 AS f FROM table1;
Другие статьи:
- Установка и настройка Oracle
- Разработка ПО на php под Oracle 10.X и MySQL
- Перенос данных с Mysql на Oracle
пятница, июня 15, 2007
Перенос данных с Mysql на Oracle

Есть самый простой вариант - это MySQL Migration Toolkit(флэш-туториал), доступен под все платформы. Очень удобно и просто.
Но если вам нужна миграция под ваши потребности и вы хотите понять как все это работает, то предлагаю сделать все самими своими руками.
Для начала создайте копию структуры базыданных с MySQL в Oracle:
mysqldump --no-data -uUSER -pPASSWORD -hHOST DATABASE
понятно, что вместо USER, PASSWORD, HOST, DATABASE вписать свои значения.
Измените вручную описание таблиц и помните, что длина имен таблиц и полей не должна превышать 30 символов:
mysql> CREATE TABLE some_table (
table_id INT(11) NOT NULL AUTO_INCREMENT,
table_field_name1 INT(11) NOT NULL,
table_field_name2 VARCHAR(255) NULL DEFAULT 'some',
table_field_name3 TEXT NOT NULL,
PRIMARY KEY (table_id),INDEX(table_field_name1)
);
Query OK, 0 rows affected (0.05 sec)
oracle> CREATE TABLE some_table (
table_id NUMBER(11) NOT NULL,
table_field_name1 NUMBER(11) NOT NULL,
table_field_name2 VARCHAR2(255) NOT NULL,
table_field_name3 VARCHAR2(4000) NOT NULL
);
Table created.
Пропало описание AUTO_INCREMENT - в Oracle для этого существует sequences(последовательности), PRIMARY KEY и INDEX - ключи создаются иначе, DEFAULT 'some' - в Oracle не совместимо понятие NULL и DEFAULT, т.е. надо выбрать либо DEFAULT 'some', либо NULL.
Изменилось описание INT на NUMBER, VARCHAR - VARCHAR2, TEXT - VARCHAR2(4000). Таблицу соответствия можно посмотреть здесь
Далее сделайте дамп данных с MySQL:
span class="code">
mysqldump --add-drop-table --default-character-set=latin1 --no-create-info --single-transaction=TRUE --add-locks=FALSE --extended-insert=FALSE -uUSER -pPASSWORD -hHOST DATABASE > data.sql
Важным параметром является single-transaction=TRUE - создаст записи по одной строчке, что позволит по строчно обработать данные с помощью sed.
- cat './tables/table1.table' \
- | sed -e 's/[`]//g' \
- | sed -e 's/^\/\*/\-\-/' \
- | sed -e 's/^LOCK TABLES/\-\-/' \
- | sed -e 's/^UNLOCK TABLES/\-\-/' \
- | sed -e \"s/'\(\-\)\{0,1\}\([0-9]\{1,\}\)\,\([0-9]\{0,2\}\)'/'\1\2.\3'/g\" \
- | sed -e \"s/'0000-00-00 00:00:00'/NULL/\" > 'tables/table1.sql'
Пояснение:
- построчное чтение
- замена всех символов `
- замена коментариев начинающихся с /* на --
- комментирование строк начинающихся с LOCK TABLES
- комментирование строк начинающихся с UNLOCK TABLES
- замена строкового значения числа вида '99,99' на 99.99 (об этом ниже)
- замена '0000-00-00 00:00:00' на NULL
В базе данных MySQL были строковые поля к которых были числа в формате 99,99 и 99.99, было решено при переходе на Oracle привести эти поля в тип NUMBER и вставка '99,99' привела бы к ошибке.
После того как данные подготовлены, внести их можно из sqlplus:
sqlplus> @./tables/table1.sql
Теперь когда целостность данных сохранена, можно создать sequences(последовательности), которые заменяют AUTO_INCREMENT в MySQL.
Создадим одну последовательность на все таблицы, и чтобы небыло дублирования id-номеров, последовательность нужно начать с максимального значения id-поля среди всех таблиц.
<?php
$oracle = array(
'name'=>'DB_NAME',
'user'=>'USER_NAME',
'password'=>'PASSWORD',
'host'=>'XXX.XXX.XXX.XXX',
'port'=>'PORT');
$mode = OCI_COMMIT_ON_SUCCESS;
$config = "
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS =
(COMMUNITY = PHP)
(PROTOCOL = TCP)
(Host = {$oracle['host']})
(Port = {$oracle['port']})
)
)
(CONNECT_DATA =
(SERVICE_NAME = {$oracle['name']})
)
)";
$link = oci_connect($oracle['user'],
$oracle['password'],
$config)
or die("Couldn't connect");
#Читаю список таблиц
$tables = file("tables.dat");
array_shift($tables);
$max_row_id = 1;
foreach($tables as $table){
$table = preg_replace("/[\r\n]/","",$table);
$sql = "SELECT * FROM $table WHERE rownum=1 ORDER BY 1 DESC \n";
$result = oci_parse($link, $sql);
oci_execute($result,$mode);
$row = oci_fetch_row($result);
if($row[0] > $max_row_id){
$max_row_id = $row[0];
}
$column_name = oci_field_name($result, 1);
#Создаем триггер для последовательности
$sql_trigger[] = "
CREATE OR REPLACE trigger \"TG_".strtoupper($table)."_BI\"
before insert on \"".strtoupper($table)."\"
for each row
begin
select \"MY_SEQ\".nextval into :NEW.".$column_name." from dual;
end TG_$table;
/";
}
print "Create sequence and start from $max_row_id+1\n";
#Удаляем последовательность
print $sql = "DROP sequence MY_SEQ ;/\n";
#Создаем последовательность
print $sql = "CREATE sequence MY_SEQ START WITH ".($max_row_id+1)." ;/\n";
#Создаем триггеры
foreach($sql_trigger as $sql){
print $sql."\n";
#$db->($sql);
}
oci_close($link);
?>
Все файлы для миграции можно скачать одним архивом здесь
Дополнительные ссылки:
Другие статьи:
- Установка и настройка Oracle
- Разработка ПО на php под Oracle 10.X и MySQL
- Перенос данных с Mysql на Oracle
вторник, ноября 14, 2006
Вложенные теги form
Недавно столкнулся с такой ситуацией, когда случайным образом у меня в php-коде получилась вложенная одна в другую форма. Причем часть полей input пропала после отправки (submit). Я не понимал в чем дело, но потом меня осенило и я задал себе вопрос "Бывают ли вложенные формы?".
Поискал в яндексе, на многих сайтах говорят что:
Я этому не поверил, рассуждая по принципу "подобия": если все парные теги могут быть
вложенными(table,b,p,font,...,etc.), то почему нет такого свойства у тега form.
Решил заглянуть в "источники" на w3c.org и ничего похожего не нашел по вложенным формам.
Примечатьльно, что в спецификации XHTML предпологается, что формы должны быть контейнерами для различных мульти-медийных, поисковых и прочих элементов, наряду с обыкновенными полями, так почему не разработать схему вложенных форм?
Поискал в яндексе, на многих сайтах говорят что:
HTML-документ может содержать в себе несколько форм, однако формы не должны находиться одна внутри другой.
Я этому не поверил, рассуждая по принципу "подобия": если все парные теги могут быть
вложенными(table,b,p,font,...,etc.), то почему нет такого свойства у тега form.
Решил заглянуть в "источники" на w3c.org и ничего похожего не нашел по вложенным формам.
Примечатьльно, что в спецификации XHTML предпологается, что формы должны быть контейнерами для различных мульти-медийных, поисковых и прочих элементов, наряду с обыкновенными полями, так почему не разработать схему вложенных форм?
четверг, ноября 09, 2006
Не используйте условия IF в MySQL запросах
При изучении почти всех языков, начинающим разработчикам говорят: "не используйте абсолютные переходы goto", кроме языка Perl, что означает "старайтесь не использовать" или
"используйте, но крайне редко". Тоже я могу сказать и про IF в MySQL: "старайтесь не использовать конструкцию IF в MySQL запросах".
Разрабатывая внутреннюю программу для фирмы на php+mysql в силу обстоятельств мне пришлось прибегнуть к такой конструкции при запросе в MySQL:
SELECT
t1.*, t2.*, t3.*
FROM
t1, t2, t3
WHERE
IF (t1.field1 = 0,
t1.field2 = t3.field2,
t1.field3 = t2.field3
)
GROUP BY t1.field3
ORDER BY DESC
Что, на первый взгляд, спасало положение, но я проверял это на 5, максимумна 10 записях в таблице. При введение в эксплуотацию этого кода при количестве записей 200 и выше, сервер MySQL просто "вешался". Страница генерировалась от 60 до 250 секунд. Сначала я грешил что я использую слишком много запросов в внешней базе данных на другом хосте, но даже исключение этих запросов не помогло.
Как я понял дело заключается в механизме IF, т.е. код просит пройти все записи таблицы t1, где поле field1 = 0
и выбрать данные или из t2 или из t3. Получается очень долгий запрос.
Пришлось потратить больше времени и переделать структуру программы. Еще раз прихожу к выводу, что нельзя экономить на времени проектировании проекта, но это уже другая тема.
"используйте, но крайне редко". Тоже я могу сказать и про IF в MySQL: "старайтесь не использовать конструкцию IF в MySQL запросах".
Разрабатывая внутреннюю программу для фирмы на php+mysql в силу обстоятельств мне пришлось прибегнуть к такой конструкции при запросе в MySQL:
SELECT
t1.*, t2.*, t3.*
FROM
t1, t2, t3
WHERE
IF (t1.field1 = 0,
t1.field2 = t3.field2,
t1.field3 = t2.field3
)
GROUP BY t1.field3
ORDER BY DESC
Что, на первый взгляд, спасало положение, но я проверял это на 5, максимумна 10 записях в таблице. При введение в эксплуотацию этого кода при количестве записей 200 и выше, сервер MySQL просто "вешался". Страница генерировалась от 60 до 250 секунд. Сначала я грешил что я использую слишком много запросов в внешней базе данных на другом хосте, но даже исключение этих запросов не помогло.
Как я понял дело заключается в механизме IF, т.е. код просит пройти все записи таблицы t1, где поле field1 = 0
и выбрать данные или из t2 или из t3. Получается очень долгий запрос.
Пришлось потратить больше времени и переделать структуру программы. Еще раз прихожу к выводу, что нельзя экономить на времени проектировании проекта, но это уже другая тема.
среда, ноября 08, 2006
Переход к другой кодировке на MySQL 5.X
Передо мной встала задача перейдти к другой кодировке в рабочей базе данных MySQL.
Первоначально данные хранились в MySQL 4.Х, но после перехода к MySQL 5 версии данные были внесены в кодировке UTF-8. Причем в MySQL 5 появилась параметр "сравнение", по умолчанию значение которого равно "latin1".
$ mysqldump database_name -uroot -p > base.sql
Установка konwert:
$ apt-get install konwert
Смотрим список доступных фильтров:
$ ls /usr/share/konwert/filters/
Конвертируем:
$ konwert UTF8-cp1251 base.sql -o base-1.sql
Конвертацию под windows можно сделать с помощью программы Штирлиц.
Заменяем текущую кодировку на нужную:
$ cat base-1.sql | awk '{gsub(/latin1/,"cp1251");print}' > base-1251.sql
где latin1 - текущая кодировка БД, cp1251 - новая кодировка.
Тоже самое можно сделать в любом редакторе простой заменой.
И результат заносим в нашу базу:
$ mysql -uroot -p -d database_name
Первоначально данные хранились в MySQL 4.Х, но после перехода к MySQL 5 версии данные были внесены в кодировке UTF-8. Причем в MySQL 5 появилась параметр "сравнение", по умолчанию значение которого равно "latin1".
$ mysqldump database_name -uroot -p > base.sql
Установка konwert:
$ apt-get install konwert
Смотрим список доступных фильтров:
$ ls /usr/share/konwert/filters/
Конвертируем:
$ konwert UTF8-cp1251 base.sql -o base-1.sql
Конвертацию под windows можно сделать с помощью программы Штирлиц.
Заменяем текущую кодировку на нужную:
$ cat base-1.sql | awk '{gsub(/latin1/,"cp1251");print}' > base-1251.sql
где latin1 - текущая кодировка БД, cp1251 - новая кодировка.
Тоже самое можно сделать в любом редакторе простой заменой.
И результат заносим в нашу базу:
$ mysql -uroot -p -d database_name
Подписаться на:
Сообщения (Atom)