3. Отдельно стоящий сайт
Внешение ресурсы • БД принадлежит только
сайту.
Бизнес-логика
• Неограниченные
привилегии.
Представление • Сайт разграничивает
База данных
права пользователей.
4. Сайт в составе
промышленной системы
Биллинг
• БД обслуживает
Коммутатор
несколько приложений
База данных
• Повышенные требова-
Инженерное ПО ния к безопасности
• Максимально
Сервер ограниченные права
клиентских
приложений
Сайт
6. Чего не хватает?
• Разграничения на
доступ к записям
• Прочих
ограничений,
обусловленных
бизнес-логикой
7. Ну например?
Если у пользователя есть права на выполнение запроса
SELECT
id
FROM
users
WHERE
login = :login
AND
password = SOME_SECRET_ALG(:password, :salt) ,
значит он может выполнить и
SELECT
id, login, password
FROM
users
8. Как быть?
• Запретить доступ к таблицам напрямую
• Предоставить абстрактный интерфейс в виде
пакетов хранимых процедур
9. Как будто ООП
БАЗА ДАННЫХ ПРИЛОЖЕНИЕ
Таблицы Данные
Пакет 1 Класс 1
процедура_1_1 метод_1_1
процедура_1_2 метод_1_2
Пакет 2 Класс 2
процедура_2_1 метод _2_1
процедура_2_2 метод _2_2
11. Недостатки подхода
• Бюрократия
• Сложность
реализации
• Отсутствие чѐтких
границ
12. Как выглядит код процедуры?
CREATE PROCEDURE AUTHENTICATE(
psUsername IN VARCHAR2,
psPassword IN VARCHAR2,
viAccount_id OUT PLS_INTEGER,
viStatus OUT VARCHAR2
)
BEGIN
SELECT
fiAccount_id,
fiStatus
INTO
viAccount_id,
viStatus
FROM
users
WHERE
fsUsername = psUsername
AND
fsPassword = SOME_SECRET_ALG(psPassword, 'some salt')
END;
14. Как выглядит код функции?
CREATE TYPE TR_ACCOUNT IS RECORD (
fiUser_id NUMBER(10),
fsCurrency_code VARCHAR2(3),
fxBalance NUMBER(10,2)
);
CREATE FUNCTION GET_ACCOUNT_INFO (
piAccount_id NUMBER
) RETURN TR_ACCOUNT IS
BEGIN
/* ... */
END;
16. Как выглядит код функции?
CREATE TYPE TN_ACCOUNTS IS TABLE OF TR_ACCOUNT;
CREATE FUNCTION GET_ACCOUNTS(
piUser_id NUMBER DEFAULT NULL
) RETURN TN_ACCOUNTS PIPELINED IS
BEGIN
/* ... */
END;
18. Рутину нужно
автоматизировать
Необходимо определить:
1. Наименования функций и
процедур
2. Наименования и типы
аргументов
3. Типы возвращаемых
значений
4. Структуру табличных
типов
19. Как получить описание
интерфейса в Oracle?
SELECT
PACKAGE_NAME, OBJECT_NAME, POSITION,
DATA_LEVEL, ARGUMENT_NAME, DATA_TYPE,
DATA_LENGTH, DEFAULTED, IN_OUT, TYPE_NAME,
TYPE_SUBNAME
FROM
USER_ARGUMENTS
WHERE
PACKAGE_NAME = 'MY_PACKAGE'
ORDER BY
PACKAGE_NAME, OBJECT_NAME, SEQUENCE, POSITION,
DATA_LEVEL;
20. Обход графа объектов
Package
Types
Procedures
TR_ACCOUNT
GET_ACCOUNT
fiUser_id
ptrResult
fsCurrency_code
piAccount_id
fxBalance
TR_ACCOUNT
S
GET_ACCOUNTS TN_ACCOUNTS
ptnResult
piUser_id
TN_ACCOUNTS
S
UPDATE_ACCOUNT
TR_ACCOUNT
21. PACKAGE_NAME
Разбор результатов
TYPE_SUBNAME
ARGUMENT_NA
OBJECT_NAME
DATA_LENGTH
DATA_LEVEL
TYPE_NAME
DEFAULTED
DATA_TYPE
POSITION
IN_OUT
ME
BILLING CREATE_NUMBER 1 0 ACCOUNT_ID BINARY_INTEGER N IN
BILLING CREATE_NUMBER 2 0 PLAN_ID BINARY_INTEGER N IN
BILLING CREATE_NUMBER 3 0 NUMBER BINARY_INTEGER N IN
BILLING CREATE_NUMBER 4 0 BEGIN_TIME DATE N IN
BILLING CREATE_NUMBER 5 0 PRODUCT_ID NUMBER 22 N OUT
BILLING CREATE_NUMBER 6 0 LOGIN VARCHAR2 N OUT
BILLING CREATE_NUMBER 7 0 PASSWORD VARCHAR2 N OUT
22. Разбор результатов
<?php
class Billing
{
public function createNumber($account_id,
$plan_id, $number, $begin_time,
&$product_id, &$login, &$password)
{
/* implementation goes here */
}
}
23. Разбор результатов
DATA_LEVEL
ARGUMENT_
PACKAGE_N
TYPE_NAME
DATA_LENG
OBJECT_NA
DEFAULTED
TYPE_SUBN
DATA_TYPE
POSITION
IN_OUT
NAME
AME
AME
ME
TH
BILLIN TN_NUMBE
BILLING GET_NUMBERS 0 0 TABLE N OUT
G RS
BILLIN TR_NUMBE
BILLING GET_NUMBERS 1 1 PL/SQL RECORD N OUT
G R
BILLING GET_NUMBERS 1 2 NUMBER_ID NUMBER 22 N OUT
BILLING GET_NUMBERS 2 2 NUMBER VARCHAR2 20 N OUT
BILLING GET_NUMBERS 3 2 BEAUTY_RATING_ID NUMBER 22 N OUT
BILLING GET_NUMBERS 4 2 BEAUTY_RATING VARCHAR2 20 N OUT
BILLING GET_NUMBERS 5 2 CATEGORY_ID NUMBER 22 N OUT
BILLING GET_NUMBERS 1 0 PREFIX VARCHAR2 Y IN
BILLING GET_NUMBERS 2 0 BEAUTY_RATING_ID NUMBER 22 Y IN
BILLING GET_NUMBERS 3 0 CATEGORY_ID NUMBER 22 Y IN
24. Разбор результатов
<?php
class Billing
{
public function getNumbers($prefix,
$beauty_rating_id = null, $category_id = null)
{
/* implementation goes here */
}
}
25. Что получается в итоге?
• Один класс для пакета — «Фасад»
• По одному классу для каждой процедуры
• Ленивая загрузка
(Zend_Loader_PluginLoader).
• Дополнительные плагины
26. Когда уже будет про Zend
Framework?
• Zend_Tool_*
• My_Tool_Provider_DbIntf
• Zend_Application_Resource_Multidb
• My_Application_Resource_DbIntf
• Zend_CodeGenerator_*
27. Но не всѐ так просто
• Необязательные аргументы
В зависимости от количества переданных
аргументов нужно формировать
соответствующий ему код SQL-запроса.
• Передача переменных по ссылке
Перегрузка методов в PHP не позволяет
передавать переменные по ссылке, поэтому
для таких методов перегрузку использовать
нельзя.
28. Повторное использование
запросов
<?php
function get_users(Zend_Db_Select $select = null)
{
if (null === $select) {
$select = Zend_Db_Select();
}
return $select
->from('users')
->query()
->fetchAll();
}
29. Что ещѐ можно
автоматизировать?
• Обработка исключительных ситуаций:
• Код ошибки
• Маркер: поле, индекс, триггер и т. д.
• Форматирование и разбор дат
• Репликация master-slave
30. Переход на PostgreSQL
• По-другому получаем описание
интерфейса
• Другой синтаксис вызова процедур
• Описание табличных типов в конечном
итоге не нужно
31. Численные показатели
• Сроки
– Две недели на прототип идеи
– Полтора года активной разработки
– Две недели на переход на PostgreSQL*
* только вызовы процедур
• Код
– Написано 621 NCLOC
– Сгенерировано 7415 NCLOC*
* для 150-ти процедур