Как стать автором
Обновить

Пишем SQL на чистом Ruby

Время на прочтение 2 мин
Количество просмотров 5.2K
Ruby — это гибкий язык, позволяющий создавать на своей основе различные DSL. Появилась идея написать DSL для SQL-запросов (для SELECT'ов), максимально приближенный к оригиналу. Кажется, получилось. Получившаяся штука называется Boroda. Далее следуют примеры кода.

require 'boroda'

sql = Boroda.build do
  from :posts, :users
  select posts.*
  where (posts.author_id == users.id) & (users.name == 'Vlad Semenov')
end

Получим:

SELECT posts.*
FROM posts, users
WHERE (posts.author_id = users.id) AND (users.name = 'Vlad Semenov')


Если кто не понял — мы пишем SQL-запросы на чистом Руби. Теперь попробуем что-нибудь посложнее.

min_rating = 5
sql = Boroda.build do
  from :posts => :p
  left join :comments => :c
  on c.post_id == p.id
  select p.id, p.title, p.content, c.id.count => :comment_count
  group by p.id
  where (p.title.like '%programming%') | # Выбираем все посты, содержащие в заголовке 'programming'
        (p.rating > min_rating) # Или с рейтингом больше 5-ти
  order by p.created_at.desc
  limit 10
  offset 20
end

Получим:

SELECT p.id, p.title, p.content, COUNT(c.id) AS comment_count
FROM posts AS p
LEFT JOIN comments AS c
ON c.post_id = p.id
WHERE (p.title LIKE '%programming%') OR (p.rating > 5)
GROUP BY p.id
ORDER BY p.created_at DESC
LIMIT 10
OFFSET 20


Теперь о том, как писать запросы при помощи Boroda. Из-за некоторых технических затруднений пришлось поменять порядок SQL-операторов. Сначало обязательно должен идти from. В нем имя таблицы указывается как символ (Symbol). Можно задавать алиасы для таблиц, если задавать их список в виде хеша, как сделано во втором примере. Далее должны следовать join'ы. Будет проще, записать возможный порядок вызова методов в следующем виде:

from tables
[[left|right] [outer|inner] join tables
on condition | using columns
[..]]
[select columns]
[ where condition
| group by columns
| having condition
| order by columns
| limit number
| offset number ]*


Другими словами все методы из последней группы вы можете вызывать в произвольном порядке. Boroda сама позаботиться о том чтобы получить правильный SQL-запрос.

Теперь подробнее про condition который используется в where и having. Использование следующих операторов имеет точно такой же смысл, какой они имеют в SQL:
+, -, *, /, >, <, >=, <=.

Из-за некоторых ограничений Руби на перегрузку операторов, пришлось немного поизвращаться:
a == b    # =>  a = b
a <=> b   # =>  a <> b
(a) & (b) # =>  (a) AND (b)
(a) | (b) # =>  (a) OR (b)

Очень важно: при использовании последних двух операторов операнды нужно ОБЯЗАТЕЛЬНО заключать в скобки, иначе в SQL-запросе может получится совсем не то, что вы ожидаете. Это вызвано тем, что данные два оператора имеют высокий приоритет в Руби.

На данный момент Boroda — это маленький модуль, генерящий SQL для SELECT-запросов. Код модуля лежит на GitHub'е. Если у кого-то возникнет желание, то Boroda может стать gem'ом.

Заранее предупреждаю, что пока не рекомендую использовать этот код в production'e, т. к. он еще не достаточно протестирован и может быть неустойчив к SQL-инъекциям.

Очень интересно услышать ваше мнение про этот DSL и получить полезные советы по улучшению :)
Теги:
Хабы:
+44
Комментарии 59
Комментарии Комментарии 59

Публикации

Истории

Работа

Программист Ruby
15 вакансий
Ruby on Rails
17 вакансий

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн