E.24. lt_sql_inspect

E.24.1. SQL 最佳实践
E.24.2. SQL 拦截

lt_sql_inspect 模块提供两个功能:SQL 最佳实践和 SQL 拦截。

E.24.1. SQL 最佳实践

E.24.1.1. 概述

该功能将提供用于规范 SQL 编写的规则。

具体而言,它分为两部分:LightDB DDL 规范检查和 LightDB DML 规范检查。

E.24.1.2. 配置参数

lightdb_best_practice_mode (String)

lightdb_best_practice_mode 用于指定用于检查 SQL 的规则。

目前支持以下规则:keyword_not_allowed,bad_datatype,must_have_pk,must_have_gmt,no_more_100cols,no_paging。 默认值为: must_have_pk.

可以在单个会话中更改此设置。

lightdb_best_practice_level (String)

lightdb_best_practice_level 用于指定检查失败时的消息级别。

它用于 SQL 最佳实践和 SQL 拦截。

可以在单个会话中更改此设置。

E.24.1.3. LightDB DDL 规范检查

在使用 DDL 语句时,LightDB 会首先检查您的语句是否符合规范。

目前,有五个检查:关键字检查(包括表名、字段名、库名和索引名)、字段类型检查(float、double、text、real、uuid、bytea、boolean、bit、character)、表是否必须包含主键、表是否包含创建时间和最后修改时间字段,以及操作的列数不超过 100。DDL 语句规范支持:

CREATE DATABASE...
CREATE TABLE...
CREATE INDEX...
ALTER TABLE...

关键字列表可以参考 "www.light-pg.com/docs/current/sql-keywords-appendix.html"

当您使用非标准语句时,将直接报告警告或错误。 例如:

create database text;
WARNING:  LightDB DDL check warn! 不允许使用关键字 [db name]:text

create table text(id int);
WARNING:  LightDB DDL check warn! 不允许使用关键字 [table name]:text
WARNING:  LightDB DDL check warn! 不允许使用关键字 [column name]:id
WARNING:  LightDB DDL check warn! 必须包含主键!
WARNING:  LightDB DDL check warn! 必须包含 gmt_create 或 gmt_modified 字段!

在这里,'text' 是一个非标准的数据库名称,因此报告了一个警告。 在创建表时,表名不是标准的,列名不是标准的,不包含主键,不包含创建时间和最后修改时间的字段,所以会发出警告。当然,还有 100 个操作列的上限,这里未列出。

默认情况下,DDL 语法检查未启用,有两种方法可以启用它。 全局修改方式是通过修改配置文件:

lightdb_best_practice_level = 'warn'
lightdb_best_practice_mode = 'all'

lightdb_best_practice_level 的值为 'warn' 表示错误级别为警告。'error' 表示错误。当检测到非标准语句时,直接报告错误:

create database text;
ERROR:  LightDB DDL check error! 不允许使用关键字 [db name]:text

如果 lightdb_best_practice_mode 配置为 'all',则表示同时检查 5 种类型。当然,您可以随意组合要配置的检查,例如:

lightdb_best_practice_mode = 'keyword_not_allowed,must_have_pk'

上述配置表示仅检查关键字和主键。

另一种配置方法是会话级别修改:

set lightdb_best_practice_level to 'warn'; 
set lightdb_best_practice_mode to 'all';

这里有五种完全开放的方式,还有另一种方式:

set lightdb_best_practice_mode to 'keyword_not_allowed,bad_datatype,must_have_pk,must_have_gmt,no_more_100cols';

E.24.1.4. LightDB DML 规范检查

在使用 DML 语句时,LightDB 会首先检查您的语句是否符合规范。

目前,只有一个检查,即无分页检查: 当用户 SQL 没有指定查询条件或分页时,提醒用户。 下面详细描述了这些检查的用法。

  • no_paging check。 在以下场景中需要检查:

    • 查询语句没有 where 条件、没有 limit 或没有 offset

    • 更新语句没有 where 条件。

    • 删除语句没有 where 条件。

如何使用它?需要在配置文件中配置 lightdb_best_practice_levellightdb_best_practice_mode

默认情况下,DML 语法检查未启用。 全局修改方式是通过修改配置文件:

lightdb_best_practice_level = 'error'
lightdb_best_practice_mode = 'all'

lightdb_best_practice_level 的值为 'warn' 表示错误级别为警告。'error' 表示错误。当检测到非标准语句时,直接报告错误:

select * from table1;
ERROR:  LightDB DML check error! select 语句中缺少 where/limit/offset 子句!

如果 lightdb_best_practice_mode 配置为 'all',则表示同时检查所有类型。当然,您可以随意组合要配置的检查,例如:

lightdb_best_practice_mode = 'no_paging'

E.24.2. SQL 拦截

E.24.2.1. 概述

此功能将拦截与 lt_sql_inspect.secure_mode 中指定规则匹配的风险 SQL。

仅操作指定数据库的 SQL 将被阻止,使用 lt_sql_inspect.secure_enable_db 指定数据库。

您可以通过 lt_sql_inspect.secure_enable_time 定义时间段,在此时间段内才会拦截 SQL。时间段仅限于一天内。

lightdb_best_practice_level 控制拦截 SQL 时的日志级别,默认为 'warn'。 lightdb_best_practice_level 的值为 'warn' 表示错误级别为警告。'error' 表示错误。

详细规则请参阅 lt_sql_inspect.secure_mode

E.24.2.2. 配置参数

lt_sql_inspect.secure_mode (String)

lt_sql_inspect.secure_mode 指定了一些 SQL 执行规则,例如拦截符合条件的 SQL。 您可以指定多个规则,用逗号分隔。 支持以下规则:select_without_where、update_without_where、delete_without_where、no_paging、high_risk_ddl。

可以在单个会话中更改此设置。

  • select_without_where:阻止仅查询一个表且没有 where 条件、没有 limit 和没有 offset 的 select 查询。

  • update_without_where:阻止仅查询一个表且没有 where 条件、没有 limit 和没有 offset 的 update 查询。

  • delete_without_where:阻止仅查询一个表且没有 where 条件、没有 limit 和没有 offset 的 delete 查询。

  • no_paging:相当于同时使用 select_without_where、update_without_where 和 delete_without_where。

  • high_risk_ddl:阻止以下 ddl:drop table、truncate table、alter table add/drop column、alter table add/drop Constraint、alter table modify column(null/not null, default/no default, type)。

lt_sql_inspect.secure_enable_time (String)

lt_sql_inspect.secure_enable_time 指定时间段,在此时间段内才会拦截 SQL。

格式为 'start_time - end_time'(例如:'09:00 - 18:00')。

此参数只能在 lightdb.conf 文件或服务器命令行中设置,并可重新加载。

lt_sql_inspect.secure_db (String)

lt_sql_inspect.secure_enable_db 指定数据库,仅在这些数据库上才会拦截 SQL。

您可以指定多个数据库,用逗号分隔。

此参数只能在 lightdb.conf 文件或服务器命令行中设置,并可重新加载。

E.24.2.3. 规则

E.24.2.3.1. select_without_where

如果一个DQL仅涉及一个表,且没有where、limit和offset语句,则会被阻塞。

查询pg_catalog、lt_catalog或ltcluster方案下的表时,SQL将不会被阻塞。 需要注意的是,当lightdb_best_practice_level设置为error时,查询其他模式下符合要求的表(包括扩展创建的表)将会报错。

 lightdb@lt_test=# set lt_sql_inspect.secure_mode=select_without_where;
 SET
 lightdb@lt_test=# select * from t1;
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 lightdb@lt_test=# select * from (select * from t1);
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 lightdb@lt_test=# select * from t1 limit 1;
 key1 | key2 
 ------+------
 (0 rows)
 
 lightdb@lt_test=# select * from t1 offset 1;
 key1 | key2 
 ------+------
 (0 rows)
 
      

涉及多个表的DQL不会被阻塞。

 lightdb@lt_test=# select * from t1, t2;
 key1 | key2 | key1 
 ------+------+------
 (0 rows)
 
      

这同样适用于视图,就像适用于子查询一样。

 lightdb@lt_test=# create view t1_v as select * from t1;
 CREATE VIEW
 lightdb@lt_test=# select * from t1_v;
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 lightdb@lt_test=# select * from t1_v where key1=1;
 key1 | key2 
 ------+------
 (0 rows)
 
      
E.24.2.3.2. update_without_where

与select_without_where类似,但针对update语句。

 lightdb@lt_test=# set lt_sql_inspect.secure_mode = 'update_without_where';
 SET
 lightdb@lt_test=# update t1 set key1=1;
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 lightdb@lt_test=# update t1 set key1=1 limit 1;
 UPDATE 0
 lightdb@lt_test=# update t1 set key1=1 offset 1;
 UPDATE 0
 lightdb@lt_test=# update t1 set key1=1 from t2 ;
 UPDATE 0
 lightdb@lt_test=# update t1 set key1=1 from t2 where t1.key1=t2.key1;
 UPDATE 0
      
E.24.2.3.3. delete_without_where

与select_without_where类似,但针对delete语句。

 
 lightdb@lt_test=# set lt_sql_inspect.secure_mode = 'delete_without_where';
 SET
 lightdb@lt_test=# delete from t1 ;
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 lightdb@lt_test=# delete from t1 where key1=1;
 DELETE 0
 lightdb@lt_test=# delete from t1 limit 1;
 DELETE 0
 lightdb@lt_test=# delete from t1 offset 1;
 DELETE 0
 lightdb@lt_test=# delete from t1 using t2;
 DELETE 0
 lightdb@lt_test=# delete from t1 using t2 where t1.key1=t2.key1;
 DELETE 0
      
E.24.2.3.4. no_paging

等价于同时使用select_without_where、update_without_where和delete_without_where。

 lightdb@lt_test=# set lt_sql_inspect.secure_mode=no_paging;
 SET
 lightdb@lt_test=#   
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 lightdb@lt_test=# update t1 set key1=1;
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 lightdb@lt_test=# delete from t1 ;
 ERROR:  lt_sql_inspect: SQL cannot execute, because a full table scan or modification may be performed on the table "t1"
 
      
E.24.2.3.5. high_risk_ddl

目前以下DDL将被视为高风险DDL,并将被拦截。

  • DROP TABLE

     lightdb@lt_test=# set lt_sql_inspect.secure_mode = high_risk_ddl;
     设置
     lightdb@lt_test=# drop table t1;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
    
  • TRUNCATE TABLE

     lightdb@lt_test=# set lt_sql_inspect.secure_mode = high_risk_ddl;
     设置
     lightdb@lt_test=# truncate table t1;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
    
  • ALTER TABLE ADD COLUMN

     lightdb@lt_test=# set lt_sql_inspect.secure_mode = high_risk_ddl;
     设置
     lightdb@lt_test=# alter table t1 add column key3 int;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
    
  • ALTER TABLE DROP COLUMN

     lightdb@lt_test=# set lt_sql_inspect.secure_mode = high_risk_ddl;
     设置
     lightdb@lt_test=# alter table t1 drop column key1;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
    
  • ALTER TABLE ADD CONSTRAINT

     lightdb@lt_test=# set lt_sql_inspect.secure_mode = high_risk_ddl;
     设置
     lightdb@lt_test=# alter table t1 add constraint t_uq unique(key1);
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
    
  • ALTER TABLE DROP CONSTRAINT

     lightdb@lt_test=# set lt_sql_inspect.secure_mode = high_risk_ddl;
     设置
     lightdb@lt_test=# alter table t1 drop constraint t_uq;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
    
  • ALTER TABLE MODIFY/ALTER COLUMN(仅以下示例中的类型将被阻止,包括null/not-null,default/non-default,以及type)。

     lightdb@lt_test=# set lt_sql_inspect.secure_mode = high_risk_ddl;
     设置
     lightdb@lt_test=# alter table t1 modify key2 bigint;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 alter  key2 type text;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 modify key2 null;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 alter  key2 drop not null;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 modify key2 not null;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 alter  key2 set not null;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 modify key2 default 10;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 alter  key2 set default 10;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#
     lightdb@lt_test=# alter table t1 alter  key2 drop default;
     错误: lt_sql_inspect: SQL无法执行,因为它是高风险DDL
     lightdb@lt_test=#