Квасной прав; триггер был бы лучшим способом для этого.
Вот код:
СОЗДАТЬ ИЛИ ЗАМЕНИТЬ ФУНКЦИЮ ensce_photo_count () RETURNS trigger AS $$ DECLARE max_photo_count INTEGER: = 10 ; photo_count INTEGER: = 0; must_check BOOLEAN: = false; BEGIN IF TG_OP = 'INSERT' THEN must_check: = true; END IF; IF TG_OP = 'UPDATE' THEN IF (NEW.owner! = OLD.owner) THEN must_check: = true; END IF; END IF; IF must_check THEN - предотвращать одновременные вставки из нескольких транзакций LOCK TABLE photos В ЭКСКЛЮЗИВНОМ РЕЖИМЕ; SELECT INTO photo_count COUNT (*) FROM photos WHERE owner = NEW.owner; IF photo_count & gt; = max_photo_count ТОГДА ИСКЛЮЧЕНИЕ ПОДЪЕМА «Невозможно вставить больше, чем% фотографий для каждого пользователя.», Max_photo_count; END IF; END IF; ВОЗВРАТ НОВЫЙ; КОНЕЦ; $$ LANGUAGE plpgsql; CREATE TRIGGER enforce_photo_count ПЕРЕД ВСТАВКОЙ ИЛИ ОБНОВЛЕНИЕМ НА ФОТОГРАФИЯХ ДЛЯ КАЖДОЙ ПРОЦЕДУРЫ ROW EXECUTE enforce_photo_count ();
Я включил блокировку таблицы, чтобы избежать ситуаций, когда два одновременных tansactions будут считать фотографии для пользователя, см., что текущий счетчик равен 1 ниже предела, а затем оба вставки, которые могли бы вызвать вы должны перейти 1 за пределы. Если это вас не волнует, было бы лучше удалить блокировку, поскольку она может стать узким местом со множеством вставок / обновлений.
В C ++ вам нужно специально объявить функцию как virtual
:
class BaseClass {
virtual void speak () {
...
}
};
В C ++ метод можно переопределить, если он был отмечен virtual
. Вы можете думать о virtual
как синониме «overridable».
Ключевое слово virtual
должно появиться в базовом классе. Он также может появиться необязательно в подклассах в точке переопределения, но это не обязательно.
Если вы используете компилятор, который поддерживает C ++ 11 (и вы должны, если вы изучаете C ++) , Я рекомендую вам всегда использовать новое ключевое слово override
, когда вы хотите переопределить:
class Base {
public:
virtual void speak() {
std::cout << "Base";
}
};
class Derived : public Base {
public:
void speak() override { // <---
std::cout << "Derived";
}
};
Если этот метод не является переопределением, компилятор скажет вам об этом, указав ошибку.
Не всегда очевидно, что первое чтение проверяется, является ли метод переопределением. Например, правильная ковариантность возвращаемого типа:
class A {};
class B : public A {};
class Base {
public:
virtual A* foo() {
return nullptr;
}
};
class Derived : public Base {
public:
B* foo() override {
return nullptr;
}
};
Это может быть не очень полезно очень часто, но override
дает понять, что кто-то должен ее прочитать.
Кроме того, если у вас есть хотя бы один виртуальный метод в вашем классе, также сделать его деструктор виртуальным. Это гарантирует, что все деструкторы будут работать при необходимости, и все будет очищено правильно:
class App {
public:
App() {}
virtual ~App() {} // <---
void run() {
this->speak();
}
virtual void speak() {
std::cout << "Base\n";
};
};