dil: (Default)
Tuesday, January 8th, 2019 02:56 am

В Debian’е 9.6 попробовал установить mysql, но вместо него поставилась mariadb.

Сервер автоматически запустился, но зайти в него от меня самого не получалось:

$ mysql -u root
ERROR 1698 (28000): Access denied for user 'root'@'localhost'

А с “-p” запрашивался пароль, но я его не знал – при установке сервера пароль ввести не просили.

Погуглил, запустил от рута mysql_secure_installation, ввёл там пароль, но клиентскую программу всё равно не пускали:

$ mysql -u root -p
Enter password: 
ERROR 1698 (28000): Access denied for user 'root'@'localhost'

А вот когда попробовал запустить mysql от рута, он успешно подключился к серверу вообще без пароля:

# mysql
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 1
Server version: 10.1.37-MariaDB-0+deb9u1 Debian 9.6

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]>

Посмотрел в таблицу mysql.user, там вполне был root@localhost с паролем и полными привилегиями, как обычно.

Ещё погуглил, и запустил там

GRANT ALL PRIVILEGES on *.* to 'root'@'localhost' IDENTIFIED BY 'ТОТжеСАМЫЙпароль';
FLUSH PRIVELEGES;

и хотя в mysql.user вроде ничего не поменялось, но “mysql -u root -p” стал работать от моего эккаунта.
А от рута просто так работать перестал:

# mysql 
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

Только с -p, и с тем же паролем работает, как обычно в mysql’е.

Оригинал этой записи в личном блоге.

dil: (Default)
Thursday, June 22nd, 2017 04:20 pm

ну и починить, чтоб работали. Зашёл посмотреть, а.. сегодня они на всех серверах уже сработали. Магия??
Ну явно кто-то что-то там поменял. Стал смотреть, кто на эти машины заходил — оказалось, что за последнюю пару месяцев только я туда и заходил – вчера и сегодня..
Но я ж там с бэкапами ещё ничего не делал, только NTP вчера проверял на куче машин, включая эти. В процессе обнаружилось, что NTP-демоны там везде есть, но почему-то не работают. Попробовал руками ntpdate запустить, а он не может преобразовать имена серверов в IP-адреса.. Пошёл копать, оказалось, что файрвол на маршрутизаторе в интернеты почему-то не разрешал этим машинкам наружу выходить, так что они не могли достучаться ни до DNS-, ни до NTP-серверов. Ну я его подправил, всё заработало.

Выходит, что это и на бэкапы как-то повлияло. Но как же? Там же всё на локальные диски складывается.

Read the rest of this entry » )

Оригинал этой записи в личном блоге.

dil: (Default)
Tuesday, June 21st, 2016 09:19 pm

Это такая система для аналитических запросов к гигантским базам данных, обновляемым в реальном времени. Изначально разрабатывалась для работы с Яндекс.Метрикой.
Брать тут, статья на хабре: https://m.habr.com/en/company/yandex/blog/303282/

P.S. Посмотрев на первую ссылку, я внезапно узнал, что у Яндекса уже почти два года как есть собственный домен первого уровня: yandex. У гугла, кстати, тоже: google. Это такая новая мода?

Оригинал этой записи в личном блоге.

dil: (Default)
Saturday, August 23rd, 2014 12:51 pm

а студент-двоечник.

Категории и тэги лежат в одной таблице. Зачем — непонятно, но это ещё фигня. Таблица выглядит так:

CREATE TABLE `wp_terms` (
  `term_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL DEFAULT '',
  `slug` varchar(200) NOT NULL DEFAULT '',
  `term_group` bigint(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`term_id`),
  UNIQUE KEY `slug` (`slug`),
  KEY `name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Что такое term_group — загадка природы, там у всех записей 0.
slug — это URL-encoded name. Им лень было при обработке запросов в /tag/ и /category/ сделать url-decode и поискать по имени, лучше сделать лишнее поле. Причём поле это такой же длины, как и name, а один неанглоязычный символ из name в url-encoded виде занимает 6 байт, например буква “л” выглядит в slug как %d0%bb . А вот почему slug сделан уникальным ключом, а name неуникальным — загадка природы.

А тип записей в wp_terms вообще отсутствует. Он ВНЕЗАПНО лежит в другой таблице:

CREATE TABLE `wp_term_taxonomy` (
  `term_taxonomy_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `term_id` bigint(20) unsigned NOT NULL DEFAULT '0',
  `taxonomy` varchar(32) NOT NULL DEFAULT '',
  `description` longtext NOT NULL,
  `parent` bigint(20) unsigned NOT NULL DEFAULT '0',
  `count` bigint(20) NOT NULL DEFAULT '0',
  PRIMARY KEY (`term_taxonomy_id`),
  UNIQUE KEY `term_id_taxonomy` (`term_id`,`taxonomy`),
  KEY `taxonomy` (`taxonomy`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Вот в ней в поле taxonomy хранит тип записи. В текстовом виде: “category”, “post_tag” или “link_category”. И в сочетании с term_id оно составляет уникальный ключ. То есть, если попытаться создать и категорию, и тэг с одинаковым именем, то в wp_terms будет только одна запись, а в wp_term_taxonomy – две, с одинаковым term_id. Но это такая редкость, что экономить единичные записи в таблице, существенно усложняя при этом поиск по имени (join двух таблиц вместо простого поиска в одной) — это идиотизм.

Что есть parent — я вообще не понял, там всегда 0. А count (руки оторвать за использование названий стандартных функций в качестве имён полей) по идее должен хранить количество постов, привязанных к данной категории или тэгу. И для большинства записей это действительно так. Но иногда..

mysql> select term_taxonomy_id, count from wp_term_taxonomy where term_taxonomy_id = 1;
+------------------+-------+
| term_taxonomy_id | count |
+------------------+-------+
|                1 |  1933 |
+------------------+-------+

mysql> select count(*) from wp_term_relationships where term_taxonomy_id = 1;
+----------+
| count(*) |
+----------+
|     3708 |
+----------+

Ничего общего.. И даже если посчитать только опубликованные посты (исключить черновики и старые версии), всё равно не совпадает:

mysql> select count(*) from wp_term_relationships r join wp_posts p on r.object_id=p.ID where r.term_taxonomy_id = 1 and p.post_status='publish';
+----------+
| count(*) |
+----------+
|     1936 |
+----------+

Оригинал этой записи в личном блоге.

dil: (Default)
Wednesday, August 20th, 2014 09:01 pm

и убеждаться, что похапе серьёзно разрушает моск.

mysql> desc wp_options;
+--------------+---------------------+------+-----+---------+----------------+
| Field        | Type                | Null | Key | Default | Extra          |
+--------------+---------------------+------+-----+---------+----------------+
| option_id    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| blog_id      | int(11)             | NO   |     | 0       |                |
| option_name  | varchar(64)         | NO   | UNI |         |                |
| option_value | longtext            | NO   |     | NULL    |                |
| autoload     | varchar(20)         | NO   |     | yes     |                |
+--------------+---------------------+------+-----+---------+----------------+

mysql> select autoload,count(*) from wp_options group by autoload;
+----------+----------+
| autoload | count(*) |
+----------+----------+
| no       |       27 |
| yes      |      243 |
+----------+----------+

20 байт для хранения булевской переменной — это что надо курить, чтоб так сделать?!

Оригинал этой записи в личном блоге.

dil: (Default)
Tuesday, August 19th, 2014 08:03 pm

Пытаюсь разобраться в структуре базы wordpress’а… Там есть таблица wp_comments, в ней, очевидно, лежат комментарии. Сотня:

mysql> select count(*) from wp_comments;
+----------+
| count(*) |
+----------+
|      101 |
+----------+

А ещё есть таблица wp_commentmeta, в которой лежит в шесть раз больше не пойми чего..

mysql> select count(*) from wp_commentmeta;
+----------+
| count(*) |
+----------+
|      667 |
+----------+

mysql> desc wp_commentmeta;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| meta_id    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| comment_id | bigint(20) unsigned | NO   | MUL | 0       |                |
| meta_key   | varchar(255)        | YES  | MUL | NULL    |                |
| meta_value | longtext            | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+

meta_key там везде одинаковый:

mysql> select distinct meta_key from wp_commentmeta;
+-----------------------+
| meta_key              |
+-----------------------+
| _wp_trash_meta_status |
+-----------------------+

А значений у него целых два, 0 и 1:

mysql> select distinct meta_value from wp_commentmeta;
+------------+
| meta_value |
+------------+
| 1          |
| 0          |
+------------+

Но при этом имеющиеся там comment_id совершенно не совпадают с теми, что есть в wp_comments:

mysql> select count(*) from wp_commentmeta where comment_id in (select comment_id from wp_comments);
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

Я фигею, дорогая редакция.

Оригинал этой записи в личном блоге.

dil: (Default)
Saturday, August 16th, 2014 12:17 pm

В одной базе данных есть две таблицы, связанные отношением 1:M. Скажем, в одной лежат посты из блога, а в другой комментарии, привязанные к постам по id.
Задача: перетащить их все в другую базу, в которой идентификаторы у постов изменятся (потому что там уже есть другие посты с теми же id, что в первой базе), но при этом сохранить привязку к ним комментариев.

С постами понятно, SELECT из первой базы и INSERT во вторую.

А вот как быть с комментариями?
Мне пока пришли в голову три идеи, но все неэффективные.

1) Для каждого поста сразу после помещения его во вторую базу, когда уже известны и старый, и новый id, найти в первой базе все комментарии по старому id и сложить их во вторую базу с новым id. Но это отдельный SQL-запрос на каждый пост, а их там дофига.

2) Сначала перенести все посты, попутно составляя табличку со старыми и новыми id, а потом переносить комментарии, заменяя id постов по этой табличке. Но если табличку держать в памяти, она получится очень нефигового размера. А в базах её создавать нельзя, правов не хватает.

3) Извлечь вместе с постами сразу все их комментарии путём LEFT OUTER JOIN. Но тогда каждый пост извлечётся целиком много раз, с каждым своим комментарием, это дикий перерасход памяти в SQL-сервере.

Как бы это сделать поэффективнее?

Оригинал этой записи в личном блоге.

dil: (Default)
Monday, January 21st, 2013 12:43 pm
mysql> select count(*) from ext_requests;
+----------+
| count(*) |
+----------+
|  7460166 |
+----------+
1 row in set (11 min 34.88 sec)

Машина больше ничем не занималась, кроме обработки этого самого запроса.

Вот это тоже радует:

mysql> show tables;
+--------------------------------------+
| Tables_in_*****************          |
+--------------------------------------+
...
+--------------------------------------+
1586 rows in set (0.02 sec)

 

Оригинал этой записи в личном блоге.

Tags:
dil: (Default)
Wednesday, November 7th, 2012 12:04 pm

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

С таким подробным отчётом локализация ошибки становится исключительно простым делом.

Но.. при всём при этом ошибка установки соединения с базой исключения не генерирует. Сообщение об этом событии даже не пишется в лог, а всего лишь выплёвывается в клиента оператором echo. А исключение случается потом, когда программа пытается послать SQL-запрос через это неоткрытое соединение.

Видать, аффтары ещё не дочитали книжку до того места, где рассказывается о логике работы с базами данных.

Оригинал этой записи в личном блоге.

dil: (Default)
Sunday, June 5th, 2011 10:44 pm

Ну конечно, я на них наступил.

Типичная последовательность действий при работе с базой данных: prepare(), execute(), если надо прочитать результат – fetch(), ну и в конце finish(). И до сих пор всё работало.

И тут ВНЕЗАПНО оказалось, что если выполняется не обычный SQLный оператор, а хранимая процедура (посредством EXECUTE PROCEDURE), то возникшая при выполнении ошибка после execute() никак не проявляется. Типа, всё нормально выполнилось. А вот когда потом делаешь fetch(), тут-то ошибка и вылезает. Более того, если процедура ничего не возвращает, то ошибка не проявляется вовсе. И процедура, кажется, вообще не выполняется, пока не сделаешь fetch(). Да-да, fetch(), который заведомо ничего вернуть не может.

В документации, естественно, эта замечательная особенность не описана. И я, как обычно, на эти грабли наступил и потратил полдня, чтоб понять, какого хрена оно молча не работает.

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

dil: (Default)
Friday, June 19th, 2009 08:31 am

Это для веб-девелоперов, остальным неинтересно будет.

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

Вот в файловой системе как – приложение, конечно, может и само проверять, надо ли позволять  текущему пользователю выполнять ту или иную операцию с файлом или директорией, но администратор системы дополнительно устанавливает права на чтение/запись/…, которые приложение обойти не может.

А в веб-приложениях, работающих с базой, обычно все операции выполняются от имени одного пользователя, и разграничение полномочий целиком возлагается на приложение. Поскольку обычно этому пользователю разрешены операции модификации данных в базе, то при эксплуатации уязвимостей хакер имеет возможность поменять не только “пользовательские” данные (скажем, содержание текстов на сайте), но и “системные” (например, шаблоны, привилегии веб-пользователей и прочие, которые нормально подлежат редактированию только администраторами сайта).

Вопрос: а что мешает веб-приложению, работающему в “режиме обычного пользователя” и в “режиме администратора сайта” подключаться к базе от имени разных пользователей, которым назначены разные привилегии – первому поменьше, второму побольше? Чтобы хакер, работающий с приложением в первом режиме, в принципе не имел возможности поменять “системные” данные.

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

Понятно, что это не панацея, но как дополнительная мера защиты покатит. Но я ни в одном распространенном движке для веб-приложений такого не видел. Кто-нибудь такие знает?

P.S. Про Windows Integrated Authentication в IIS я в курсе, но у него область применения довольно узкая. Если его включить, то придётся всех пользователей заводить на уровне ОС. Для публичных веб-приложений с большим количеством пользователей это неприемлемо.

Оригинал этой записи. Комментировать можно тут или там.

Любые материалы из этого блога запрещается использовать на сайте livejournal.ru в любой форме и любом объёме

dil: (Default)
Thursday, May 28th, 2009 05:14 pm

А вот кто-нибудь может объяснить, что делает аксессовская функция datediff(“w”,,)?
Если я правильно понял описание, то она возвращает разницу между двумя временнЫми метками в… днях недели.

И конечно, я наткнулся на кусок кода, который ЭТО использует. Если найду автора, обязательно опубликую его мысли на тему, зачем ему это понадобилось.

А пока вопрос к залу: правильно ли я понял смысл этого параметра, и как ЭТО записать  в человеческих терминах?

Оригинал этой записи в личном блоге.

Tags:
dil: (Default)
Thursday, May 28th, 2009 12:06 pm

Помогите, не дайте умереть в неведении!

Как на ANSI SQL (или на Informix-диалекте) пишется _эффективный_ запрос на апдейт многих полей в одной таблице данными из другой таблицы?

Так работает, но по-моему, это сильно неэффективный метод:

UPDATE t1 SET
c1 = (SELECT c1 FROM t2 WHERE t2.id=t1.id),
c2 = (SELECT c2 FROM t2 WHERE t2.id=t1.id),
c3 = (SELECT c3 FROM t2 WHERE t2.id=t1.id),
...
WHERE id IN (SELECT id FROM t2);

Когда поле одно, это ещё ничего, а когда их десяток, это как-то некрасиво выглядит.

Upd: на информиксовском диалекте это пишется так:
UPDATE t1 SET (c1,c2,c3)=
((SELECT c1,c2,c3 FROM t2 WHERE t2.id=t1.id))

Двойные скобки у вложенного селекта существены.

Оригинал этой записи в личном блоге.

dil: (Default)
Monday, March 30th, 2009 01:28 pm
ну раз уж он сегодня случился, то:
не ленитесь в операторах SELECT и INSERT явно перечислить имена полей. Даже если их много.

Это сэкономит вам много времени и нервов.
Когда какому-нибудь доброму человеку придёт в голову ночью, тихонько, никому не говоря, поменять структуру таблицы.
Tags:
dil: (Default)
Thursday, January 22nd, 2009 03:46 pm
дата в базе данных хранится в виде строки.
в виде ДД/ММ/ГГ.

УБИЛ БЫ
dil: (Default)
Wednesday, December 10th, 2008 11:15 am
тут. Рассматриваются, натурально, проблемы структуры баз данных с точки зрения однополых браков и около. Длинно, но заслуживает прочтения :)

via [livejournal.com profile] lhovitch
Tags: