如何为 Postgresql 设计触发器,我的“Hello world”测试有什么问题?

问题描述 投票:0回答:2

我需要在 postgresql 中使用触发器(最好通过 SQLALchemy ORM)。然而,我似乎无法创建一个基地。 作为基本测试,我尝试在每次插入行时将字符串“Hello world”放入名为 test 的列中。

我这样设置桌子:

from __future__ import annotations
from typing import List
import sqlalchemy
from sqlalchemy import create_engine, ForeignKey, Column, Integer, String, CheckConstraint, MetaData
from sqlalchemy.orm import DeclarativeBase, Mapped, DeclarativeBase, MappedAsDataclass, mapped_column, relationship
from sqlalchemy.schema import DDL
import datetime

dbUrl = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

engine = create_engine(dbUrl,
                       echo=True)

metadata = MetaData()



class Base(MappedAsDataclass, DeclarativeBase):
  pass

class Company(Base):
    __tablename__ = "company"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    managers: Mapped[List[Manager]] = relationship(back_populates="company")

Company.__table__
Base.metadata.create_all(engine)

class Employee(Base):
    __tablename__ = "employee"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str]
    type: Mapped[str]
    test: Mapped[str] = mapped_column(nullable=True)

    __mapper_args__ = {
        "polymorphic_identity": "employee",
        "polymorphic_on": "type",
    }

Employee.__table__
Base.metadata.create_all(engine)


class Manager(Employee):
    __tablename__ = "manager"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    manager_name: Mapped[str]
    CheckConstraint("manager_name == employee.name", name="check1")

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="managers")

    __mapper_args__ = {
        "polymorphic_identity": "manager",
    }

class Engineer(Employee):
    __tablename__ = "engineer"
    id: Mapped[int] = mapped_column(ForeignKey("employee.id"), primary_key=True)
    engineer_name: Mapped[str]

    company_id: Mapped[int] = mapped_column(ForeignKey("company.id"))
    company: Mapped[Company] = relationship(back_populates="engineers")

    __mapper_args__ = {
        "polymorphic_identity": "engineer",
    }  
Engineer.__table__
Base.metadata.create_all(engine)

然后尝试创建这样的触发器:

sqlalchemy.event.listen(metadata,
                       "before_create",
                       DDL("""CREATE TRIGGER autoCreateEmployee
                       AFTER INSERT
                       ON employee
                       FOR EACH ROW
                       INSERT INTO employee(test)
                       VALUES("Hello world"),
                       0)  
                       """))

表已创建并且没有发出错误,但据我所知,未添加触发器。我还尝试使用 DBeaver 直接在 SQL 中运行测试触发器语句:

CREATE TRIGGER autoCreateEmployee
                       AFTER INSERT
                       ON employee
                       FOR EACH ROW
                       INSERT INTO employee(test)
                       VALUES("Hello world")

这给了我这个错误:

SQL Error [42601]: ERROR: syntax error at or near "INSERT"
  Position: 165

Error position: line: 5 pos: 164

我的触发语句有什么问题?为什么我在通过 SQLAlchemy 运行时看不到错误?

编辑:我试图改编这个例子:

更新:根据评论,我再次查看了文档,并意识到我正在查看的一些示例甚至不是 Postgres。现在我可以运行命令来创建函数和触发器而不会出现错误,但据我所知,触发器存在(因为我无法在不删除的情况下重新创建它),但似乎与它所在的表没有关联应该是开启的。

目前我只是运行原始 SQL 以避免 ORM 复杂化:

drop function hello_world;

CREATE FUNCTION hello_world() RETURNS trigger AS $hello_world$
    BEGIN
        INSERT INTO employee(test) VALUES ("HELLO WORLD!");
        RETURN NEW;
    END;
$hello_world$ LANGUAGE plpgsql;

drop trigger hello on employee;

CREATE TRIGGER hello
    AFTER UPDATE ON employee
    FOR EACH ROW
    EXECUTE FUNCTION hello_world();

Edit2:删除了屏幕截图,因为它是错误的屏幕截图并且基于误解。

现状:

我的功能是这样的:

CREATE FUNCTION hello_world()
RETURNS trigger
LANGUAGE PLPGSQL
AS $hello_world$
    BEGIN
        INSERT INTO employee(test) VALUES ('HELLO WORLD!');
    END;
$hello_world$

我像这样创建触发器:

CREATE TRIGGER hello
    AFTER INSERT ON employee
    FOR EACH ROW
    EXECUTE FUNCTION hello_world();

但是,它尝试创建一个新行,其中仅包含测试列中的测试字符串,而不是创建到我刚刚输入数据的行中。我的印象是

FOR EACH ROW
意味着该函数将在插入的所有行上执行。显然我还缺少一些东西。

如果我跑步:

INSERT INTO employee (name,type) VALUES ('Merv', 'manager');

我明白了

SQL Error [23502]: ERROR: null value in column "name" of relation "employee" violates not-null constraint
  Detail: Failing row contains (31, null, null, HELLO WORLD!).
  Where: SQL statement "INSERT INTO employee(test) VALUES ('HELLO WORLD!')"
PL/pgSQL function hello_world() line 3 at SQL statement

所以现在我需要弄清楚如何使用触发器函数定位刚刚修改的行。

postgresql sqlalchemy triggers sql-insert ddl
2个回答
0
投票

这是我的触发器的工作代码。谢谢大家的耐心等待。

CREATE FUNCTION hello_world()
RETURNS trigger
LANGUAGE PLPGSQL
AS $hello_world$
    BEGIN
        NEW.test := 'HeLLo wErLd';
        RETURN NEW;
    END;
$hello_world$


CREATE TRIGGER hello
    BEFORE INSERT ON employee
    FOR EACH ROW
    EXECUTE FUNCTION hello_world();

-1
投票

可能是因为双引号。一般来说,双引号用于表或字段的名称,单引号用于字符串常量。

试试这个:

sqlalchemy.event.listen(metadata,
                       "before_create",
                       DDL("""CREATE TRIGGER autoCreateEmployee
                       AFTER INSERT
                       ON employee
                       FOR EACH ROW
                       INSERT INTO employee(test)
                       VALUES('Hello world'),
                       0)  
                       """))

还有这个:

CREATE TRIGGER autoCreateEmployee
                       AFTER INSERT
                       ON employee
                       FOR EACH ROW
                       INSERT INTO employee(test)
                       VALUES('Hello world')
© www.soinside.com 2019 - 2024. All rights reserved.