das*_*s-g 5 postgresql insert error-handling postgis
INSERT INTO
当其中一些行的 PostGIS 几何类型的值与目标表中相应行的 PostGIS 几何类型不兼容时,使用单个语句将多行插入到表中会失败(如预期的那样):
CREATE EXTENSION postgis;
CREATE TABLE t (
id integer,
p geometry(POINT)
);
INSERT INTO t
VALUES
( 1, ST_GeometryFromText('Point(0 0)') ),
( 2, ST_GeometryFromText('Point(1 2)') ),
( 3, ST_GeometryFromText('MultiPoint(2 3)') ),
( 4, ST_GeometryFromText('Point(5 23)') ),
( 5, ST_GeometryFromText('Point(42 36)') );
Run Code Online (Sandbox Code Playgroud)
错误消息告诉我们到底出了什么问题:
CREATE EXTENSION postgis;
CREATE TABLE t (
id integer,
p geometry(POINT)
);
INSERT INTO t
VALUES
( 1, ST_GeometryFromText('Point(0 0)') ),
( 2, ST_GeometryFromText('Point(1 2)') ),
( 3, ST_GeometryFromText('MultiPoint(2 3)') ),
( 4, ST_GeometryFromText('Point(5 23)') ),
( 5, ST_GeometryFromText('Point(42 36)') );
Run Code Online (Sandbox Code Playgroud)
但它缺乏有用的上下文信息,例如:
INSERT
有这个问题?我可以修改 INSERT 语句,以便 PostgreSQL 给我这些信息,例如,在错误消息中包含违规行的完整内容,就像违反NOT NULL
约束一样?
例如
CREATE TABLE s (
i integer NOT NULL, t text
);
INSERT INTO s
VALUES
(1 , 'foo'),
(NULL, 'bar'),
(2 , 'baz');
Run Code Online (Sandbox Code Playgroud)
产生更有用的消息:
ERROR: Geometry type (MultiPoint) does not match column type (Point)
Run Code Online (Sandbox Code Playgroud)
当您可以查看语句中VALUES
列出的内容INSERT
并查看违规行时,这当然不是那么重要。但是,当插入的行是从另一个表中选择或动态计算时,会出现同样的问题,然后提供更多信息的错误消息确实很有用。
解决方法(因为我不认为自己这是“答案”)
您可以使用一个函数循环遍历您尝试插入到最终表中的选择的结果,并在插入值之前通知您。如果其中一个失败,您知道是哪一个:
您创建一个函数:
CREATE OR REPLACE FUNCTION insert_one_by_one (_sql_select_txt text) RETURNS void AS
$$
DECLARE
_r t%rowtype ;
BEGIN
FOR _r IN EXECUTE _sql_select_txt LOOP
RAISE NOTICE 'Going to insert: id=%, p=%', _r.id, ST_AsText(_r.p) ;
INSERT INTO t VALUES (_r.*) ;
END LOOP ;
END ;
$$
LANGUAGE plpgsql ;
Run Code Online (Sandbox Code Playgroud)
您可以通过传递一个(格式良好的)SQL 语句的文本来调用它,该语句返回一个与以下类型相同的表t
:
SELECT insert_one_by_one
($$
SELECT * FROM
(
VALUES
( 1, ST_GeometryFromText('Point(0 0)') ),
( 2, ST_GeometryFromText('Point(1 2)') ),
( 3, ST_GeometryFromText('MultiPoint(2 3)') ),
( 4, ST_GeometryFromText('Point(5 23)') ),
( 5, ST_GeometryFromText('Point(42 36)') )
) AS v (id, p)
$$) ;
Run Code Online (Sandbox Code Playgroud)
这将产生以下输出(在 pgAdmin III 消息窗口上,或者在stdout
/ stderr
[我认为] 上,如果您使用psql
):
NOTICE: Going to insert: id=1, p=POINT(0 0)
CONTEXT: PL/pgSQL function insert_one_by_one(text) line 6 at RAISE
NOTICE: Going to insert: id=2, p=POINT(1 2)
CONTEXT: PL/pgSQL function insert_one_by_one(text) line 6 at RAISE
ERROR: Geometry type (MultiPoint) does not match column type (Point)
CONTEXT: PL/pgSQL function insert_one_by_one(text) line 5 at FOR over EXECUTE statement
********** Error **********
ERROR: Geometry type (MultiPoint) does not match column type (Point)
SQL state: 22023
Context: PL/pgSQL function insert_one_by_one(text) line 5 at FOR over EXECUTE statement
Run Code Online (Sandbox Code Playgroud)
警告:这会严重减慢您的查询速度。恕我直言,仅当原始查询失败时才应将其用作“事后”调试工具INSERT
。
奇怪的是,我无法让函数仅在出现错误时通过捕获错误来写入消息。最初的目的是使用这个功能:
CREATE OR REPLACE FUNCTION insert_one_by_one (_sql_txt text) RETURNS void AS
$$
DECLARE
_r t%rowtype ;
BEGIN
FOR _r IN EXECUTE _sql_txt LOOP
BEGIN
INSERT INTO t VALUES (_r.*) ;
EXCEPTION
WHEN SQLSTATE '22023' THEN
RAISE EXCEPTION 'Error while trying to insert id = %, p = %', _r.id, _r.p USING ERRCODE = '22023';
WHEN others THEN
RAISE EXCEPTION 'What ??' ;
END ;
END LOOP ;
END ;
$$
LANGUAGE plpgsql ;
Run Code Online (Sandbox Code Playgroud)
...但是有了它,你就会得到:
********** Error **********
ERROR: Geometry type (MultiPoint) does not match column type (Point)
SQL state: 22023
Context: PL/pgSQL function insert_one_by_one(text) line 5 at FOR over EXECUTE statement
Run Code Online (Sandbox Code Playgroud)
我想,这意味着 PostGIS 在 PostgreSQL 内部进行了相当深入的攻击。
归档时间: |
|
查看次数: |
1543 次 |
最近记录: |