Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: 添加参数disable_types可灵活配置禁用的数据类型(#400,#409) #418

Merged
merged 3 commits into from
Jan 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ on:
jobs:
build:
name: Build
runs-on: ubuntu-16.04
runs-on: ubuntu-18.04
steps:
# - name: show mysql info
# run: ls -l /etc/my* || true
Expand Down
4 changes: 3 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,9 @@ type Inc struct {
CheckReadOnly bool `toml:"check_read_only" json:"check_read_only"`

// 连接服务器的默认字符集,默认值为utf8mb4
DefaultCharset string `toml:"default_charset" json:"default_charset"`
DefaultCharset string `toml:"default_charset" json:"default_charset"`
// 禁用数据库类型,多个时以逗号分隔.该参数优先级低于enable_blob_type/enable_enum_set_bit等参数
DisableTypes string `toml:"disable_types" json:"disable_types"`
EnableAlterDatabase bool `toml:"enable_alter_database" json:"enable_alter_database"`
// 允许执行任意语法类型.该设置有安全要求,仅支持配置文件方式设置
EnableAnyStatement bool `toml:"enable_any_statement" json:"enable_any_statement"`
Expand Down
9 changes: 5 additions & 4 deletions docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,21 @@ check_identifier_lower `v1.2.5` | false | true,false | If the Identif
|check_timestamp_default|FALSE|true,false|If return error when timestamp column has no default value.|
|columns_must_have_index `v1.2.2`| |string|Set the column must be create index. Split by comma. Format: column name [type, option]|
|default_charset `v1.0.5`|utf8mb4|string|The default connection charset. Default utf8mb4.|
|disable_types `v1.2.6` | "" | string | Disable database types, separate with commas (The following parameters are automatically merged: enable_blob_type,enable_json_type,enable_enum_set_bit,enable_timestamp_type)
|enable_autoincrement_unsigned|FALSE|true,false|If the auto_increment column should be unsigned.|
|enable_any_statement `v1.2.5`|FALSE|true,false|If all SQL approved.[More](https://github.com/hanchuanchuan/goInception/pull/301)|
|enable_blob_not_null `v1.0`|FALSE|true,false|If set the default value of `blob/text/json` not null are approved, default is false, means not allowed.|
|enable_blob_type|FALSE|true,false|If check support of BLOB column, include create,alter etc.|
|enable_blob_type `Deprecated`|FALSE|true,false|If check support of BLOB column, include create,alter etc. (use `disable_types` instead)|
|enable_change_column `v1.0.3`|TRUE|true,false|If support change column syntax, default true.|
|enable_column_charset|FALSE|true,false|If allow to set charset in SQL|
|enable_drop_database|FALSE|true,false|If allow to drop database.|
|enable_drop_table|FALSE|true,false|If allow to drop table.|
|enable_enum_set_bit|FALSE|true,false|If can use enum,set,bit|
|enable_enum_set_bit `Deprecated`|FALSE|true,false|If can use enum,set,bit (use `disable_types` instead)|
|enable_fingerprint `v0.6.2`|FALSE|true,false|SQL fingerprint.|
|explain_rule `v1.1.1`|`first`|`first`, `max`|The rule which explain decide the effect of SQL. `first`: use affect rows at the first row of explain shows as the SQL affect rows. `max`: use the max affect rows of explain as the whole explain affect rows.|
|enable_foreign_key|FALSE|true,false|If can use foreign key.|
|enable_identifer_keyword|FALSE|true,false|If use MySQL key words in SQL. default warn.|
|enable_json_type `v0.7.2`|FALSE|true,false|If can use Json type include create, alter etc.|
|enable_json_type `v0.7.2` `Deprecated`|FALSE|true,false|If can use Json type include create, alter etc. (use `disable_types` instead)|
|enable_minimal_rollback `v1.1.2`|FALSE|true,false|If turn on the min rollback SQL, if on the rollback of update only record the change rows. Default false.|
|enable_nullable|TRUE|true,false|If allow NULL for new column.|
|enable_null_index_name v0.7.1|FALSE|true,false|If allow NULL index name for new index.|
Expand All @@ -71,7 +72,7 @@ check_identifier_lower `v1.2.5` | false | true,false | If the Identif
|enable_set_collation `v0.7`|FALSE|true,false|If enable setting collation|
|enable_set_engine `v1.0-rc4`|TRUE|true,false|If enable setting engine, default true.|
|enable_sql_statistic `v0.9`|FALSE|true,false|Turn on statistic|
|enable_timestamp_type `v1.0.1`|TRUE|true,false|If enable timestamp column, include create and alter, default true.|
|enable_timestamp_type `v1.0.1` `Deprecated`|TRUE|true,false|If enable timestamp column, include create and alter, default true. (use `disable_types` instead)
|enable_use_view `v1.2.4`|FALSE|true,false|If enable create and use View|
|enable_zero_date `v1.0.1`|TRUE|true,false|If enable time is 0, when turn off return error. Default true, means turn on, followed `NO_ZERO_DATE` in `sql_mode` setting.|
|general_log `v0.8.1`|FALSE|true,false|If record full log|
Expand Down
9 changes: 5 additions & 4 deletions docs/zh/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,21 @@ check_timestamp_count `v0.6.0` | false | true,false | 配置是
check_timestamp_default | false | true,false | 建表时,如果没有为timestamp类型指定默认值,则报错
columns_must_have_index `v1.2.2` | "" | string | 指定的列必须添加索引。多个列时以逗号分隔(`格式: 列名 [列类型,可选]`),指定列类型时对类型也会限制.
default_charset `v1.0.5` | "utf8mb4" | string | 设置连接数据库的默认字符集,默认值为`utf8mb4` (解决低版本不支持utf8mb4的问题)
disable_types `v1.2.6` | "" | string | 禁用数据库类型,多个时以逗号分隔(合并参数 enable_blob_type,enable_json_type,enable_enum_set_bit,enable_timestamp_type)
enable_autoincrement_unsigned | false | true,false | 自增列是不是要为无符号型
enable_any_statement `v1.2.5` | false | true,false | 是否允许所有语法(仍受其他开关影响,如删表等).[详细说明见PR](https://github.com/hanchuanchuan/goInception/pull/301)
enable_blob_not_null `v1.0` | false | true,false | 是否允许blob/text/json类型置为`not null`,默认为`false`,即不允许
enable_blob_type | false | true,false | 检查是不是支持BLOB字段,包括建表、修改列、新增列操作
enable_blob_type `已弃用` | false | true,false | 检查是不是支持BLOB字段,包括建表、修改列、新增列操作 (使用参数`disable_types`代替)
enable_change_column `v1.0.3` | true | true,false | 设置是否支持change column语法,默认值`true`
enable_column_charset | false | true,false | 允许列自己设置字符集
enable_drop_database | false | true,false | 是否允许删除数据库
enable_drop_table | false | true,false | 是否允许删除表
enable_enum_set_bit | false | true,false | 是不是支持enum,set,bit数据类型
enable_enum_set_bit `已弃用` | false | true,false | 是不是支持enum,set,bit数据类型 (使用参数`disable_types`代替)
enable_fingerprint `v0.6.2` | false | true,false | sql指纹功能。dml语句相似时,可以根据相同的指纹ID复用explain结果,以减少远端数据库explain操作,并提高审核速度
explain_rule `v1.1.1` | "first" | "first", "max" | explain判断受影响行数时使用的规则(`"first", "max"`)。 `"first"`: 使用第一行的explain结果作为受影响行数, `"max"`: 使用explain结果中的最大值作为受影响行数
enable_foreign_key | false | true,false | 是不是支持外键
enable_identifer_keyword | false | true,false | 检查在SQL语句中,是不是有标识符被写成MySQL的关键字,默认值为报警。
enable_json_type `v0.7.2` | false | true,false | 设置是否允许json类型字段,包括建表、修改列、新增列操作
enable_json_type `v0.7.2` `已弃用` | false | true,false | 设置是否允许json类型字段,包括建表、修改列、新增列操作 (使用参数`disable_types`代替)
enable_minimal_rollback `v1.1.2` | false | true,false | 设置是否启用最小化回滚SQL,当开启时,update的回滚语句仅记录最小化变更(未变更列不再记录), 默认为`false`
<s>enable_not_innodb `v1.0-rc4 已删除`</s> | false | true,false | `已删除!` 请使用 `enable_set_engine`和 `support_engine`以便于更灵活的指定存储引擎。 <s> *建表指定的存储引擎不为Innodb,不报错* </s>
enable_nullable | true | true,false | 创建或者新增列时是否允许列为NULL
Expand All @@ -74,7 +75,7 @@ enable_set_charset | false | true,false | 是否允许指定表
enable_set_collation `v0.7` | false | true,false | 是否允许指定表和数据库的排序规则
enable_set_engine `v1.0-rc4` | true | true,false | 是否允许指定存储引擎,默认为`true`
enable_sql_statistic `v0.9` | false | true,false | 开启统计功能. 详见 **[统计功能](statistics.html)**
enable_timestamp_type `v1.0.1` | true | true,false | 设置是否允许 `timestamp` 类型字段,包括建表、修改列、新增列操作,默认为 `true`
enable_timestamp_type `v1.0.1` `已弃用` | true | true,false | 设置是否允许 `timestamp` 类型字段,包括建表、修改列、新增列操作,默认为 `true` (使用参数`disable_types`代替)
enable_use_view `v1.2.4` | false | true,false | 支持创建和使用视图
enable_zero_date `v1.0.1` | true | true,false | 设置是否支持时间为0值,关闭时强制报错。默认值为 `true`,即开启,此时会基于数据库sql_mode的NO_ZERO_DATE判断是否支持
general_log `v0.8.1` | false | true,false | 是否记录全量日志
Expand Down
1 change: 1 addition & 0 deletions session/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func (s *session) init() {

s.backupDBCacheList = make(map[string]bool)
s.backupTableCacheList = make(map[string]bool)
s.disableTypes = make(map[string]struct{})

s.inc = config.GetGlobalConfig().Inc
s.osc = config.GetGlobalConfig().Osc
Expand Down
6 changes: 2 additions & 4 deletions session/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ var ErrorsDefault = map[ErrorCode]string{
ER_ID_IS_UPER: "Identifier is not allowed to been upper-case.",
ErrUnknownCharset: "Unknown charset: '%s'.",
ER_UNKNOWN_COLLATION: "Unknown collation: '%s'.",
ER_INVALID_DATA_TYPE: "Not supported data type on field: '%s'.",
ER_INVALID_DATA_TYPE: "Not supported data type on field: '%s'(%s).",
ER_NOT_ALLOWED_NULLABLE: "Column '%s' in table '%s' is not allowed to been nullable.",
ER_DUP_FIELDNAME: "Duplicate column name '%s'.",
ER_WRONG_COLUMN_NAME: "Incorrect column name '%s'.",
Expand Down Expand Up @@ -434,7 +434,7 @@ var ErrorsChinese = map[ErrorCode]string{
ER_ID_IS_UPER: "标识符不允许大写.",
ErrUnknownCharset: "未知的字符集: '%s'.",
ER_UNKNOWN_COLLATION: "未知的排序规则: '%s'.",
ER_INVALID_DATA_TYPE: "列 '%s' 数据类型不支持.",
ER_INVALID_DATA_TYPE: "列 '%s' 数据类型(%s)不支持.",
ER_NOT_ALLOWED_NULLABLE: "列 '%s' 不允许为null(表 '%s').",
ER_DUP_FIELDNAME: "重复的列名: '%s'.",
ER_WRONG_COLUMN_NAME: "不正确的列名: '%s'.",
Expand Down Expand Up @@ -1200,10 +1200,8 @@ func TestCheckAuditSetting(cnf *config.Config) {

if !cnf.Inc.EnableEnumSetBit {
cnf.IncLevel.ER_USE_ENUM = int8(GetErrorLevel(ER_USE_ENUM))
cnf.IncLevel.ER_INVALID_DATA_TYPE = int8(GetErrorLevel(ER_INVALID_DATA_TYPE))
} else {
cnf.IncLevel.ER_USE_ENUM = 0
cnf.IncLevel.ER_INVALID_DATA_TYPE = 0
}

if cnf.Inc.CheckIndexPrefix {
Expand Down
4 changes: 4 additions & 0 deletions session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ type Session interface {

// 用以测试
GetAlterTablePostPart(sql string, isPtOSC bool) string
InitDisableTypes()

LoadOptions(opt SourceOptions) error
Audit(ctx context.Context, sql string) ([]Record, error)
Expand Down Expand Up @@ -263,6 +264,9 @@ type session struct {

// masking 语法树解析功能
maskingFields []MaskingFieldInfo

// 统一处理禁用的数据类型
disableTypes map[string]struct{}
}

func (s *session) getMembufCap() int {
Expand Down
73 changes: 57 additions & 16 deletions session/session_inception.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ func (s *session) executeInc(ctx context.Context, sql string) (recordSets []sqle
s.sqlFingerprint = make(map[string]*Record, 64)
}

s.initDisableTypes()
continue
case *ast.InceptionCommitStmt:

Expand Down Expand Up @@ -645,6 +646,8 @@ func (s *session) processCommand(ctx context.Context, stmtNode ast.StmtNode,
_, err := s.executeInceptionSet(node, currentSql)
if err != nil {
s.appendErrorMessage(err.Error())
} else {
s.initDisableTypes()
}
} else {
return s.executeInceptionSet(node, currentSql)
Expand Down Expand Up @@ -3971,18 +3974,19 @@ func (s *session) checkVarcharLength(t *TableInfo, colDef *ast.ColumnDef) {
}
}
}

func (s *session) mysqlCheckField(t *TableInfo, field *ast.ColumnDef, alterTableType ast.AlterTableType) {
log.Debug("mysqlCheckField")

tableName := t.Name
if !s.inc.EnableEnumSetBit && (field.Tp.Tp == mysql.TypeEnum ||
field.Tp.Tp == mysql.TypeSet ||
field.Tp.Tp == mysql.TypeBit) {
s.appendErrorNo(ER_INVALID_DATA_TYPE, field.Name.Name)
}

if field.Tp.Tp == mysql.TypeTimestamp && !s.inc.EnableTimeStampType {
s.appendErrorNo(ER_INVALID_DATA_TYPE, field.Name.Name)
if len(s.disableTypes) > 0 {
fieldType := types.TypeToStr(field.Tp.Tp, field.Tp.Charset)
for typeStr := range s.disableTypes {
if typeStr == fieldType {
s.appendErrorNo(ER_INVALID_DATA_TYPE, field.Name.Name, typeStr)
break
}
}
}

if field.Tp.Tp == mysql.TypeString && (s.inc.MaxCharLength > 0 && field.Tp.Flen > int(s.inc.MaxCharLength)) {
Expand Down Expand Up @@ -4120,14 +4124,15 @@ func (s *session) mysqlCheckField(t *TableInfo, field *ast.ColumnDef, alterTable
}
//是否使用 text\blob\json 字段类型
//当EnableNullable=false,不强制text\blob\json使用NOT NULL
if types.IsTypeBlob(field.Tp.Tp) {
s.appendErrorNo(ER_USE_TEXT_OR_BLOB, field.Name.Name)
} else if field.Tp.Tp == mysql.TypeJSON {
s.appendErrorNo(ErrJsonTypeSupport, field.Name.Name)
} else {
if !notNullFlag && !hasGenerated {
s.appendErrorNo(ER_NOT_ALLOWED_NULLABLE, field.Name.Name, tableName)
}

// 类型限制统一由disableTypes处理
// if types.IsTypeBlob(field.Tp.Tp) {
// s.appendErrorNo(ER_USE_TEXT_OR_BLOB, field.Name.Name)
// } else if field.Tp.Tp == mysql.TypeJSON {
// s.appendErrorNo(ErrJsonTypeSupport, field.Name.Name)
// }
if !notNullFlag && !hasGenerated {
s.appendErrorNo(ER_NOT_ALLOWED_NULLABLE, field.Name.Name, tableName)
}

// 审核所有指定了charset或collate的字段
Expand Down Expand Up @@ -8228,3 +8233,39 @@ func (s *session) checkVaildWhere(expr ast.ExprNode) bool {
}
return true
}

func (s *session) initDisableTypes() {
log.Debug("initDisableTypes")
s.disableTypes = make(map[string]struct{})
if !s.inc.EnableBlobType {
s.disableTypes["tinytext"] = struct{}{}
s.disableTypes["mediumtext"] = struct{}{}
s.disableTypes["longtext"] = struct{}{}
s.disableTypes["text"] = struct{}{}
s.disableTypes["tinyblob"] = struct{}{}
s.disableTypes["mediumblob"] = struct{}{}
s.disableTypes["longblob"] = struct{}{}
s.disableTypes["blob"] = struct{}{}
}
if !s.inc.EnableTimeStampType {
s.disableTypes["timestamp"] = struct{}{}
}
if !s.inc.EnableJsonType {
s.disableTypes["json"] = struct{}{}
}
if !s.inc.EnableEnumSetBit {
s.disableTypes["enum"] = struct{}{}
s.disableTypes["set"] = struct{}{}
s.disableTypes["bit"] = struct{}{}
}
for _, typeStr := range strings.Split(s.inc.DisableTypes, ",") {
key := strings.ToLower(strings.TrimSpace(typeStr))
if key != "" {
s.disableTypes[key] = struct{}{}
}
}
}

func (s *session) InitDisableTypes() {
s.initDisableTypes()
}
Loading