Code review comment: 1. It's strongly recommended to put thoes error messages in ErrorCode.java if the messages will be shown to users. 2. The message could be `You should specify all the key columns except auto-increment columns when inserting to a Primary Key type table. Expected key columns (#1) are: {$1}, specified key columns (#2) are: {$2} (including auto-increment columns), target table is {$3}.`

Code before review:
public static void analyze(InsertStmt insertStmt, ConnectContext session) {
QueryRelation query = insertStmt.getQueryStatement().getQueryRelation();
new QueryAnalyzer(session).analyze(insertStmt.getQueryStatement());
List<Table> tables = new ArrayList<>();
AnalyzerUtils.collectSpecifyExternalTables(insertStmt.getQueryStatement(), tables, Table::isHiveTable);
tables.stream().map(table -> (HiveTable) table)
.forEach(table -> table.useMetadataCache(false));
/*
*  Target table
*/
Table table;
if (insertStmt.getTargetTable() != null) {
table = insertStmt.getTargetTable();
} else {
table = getTargetTable(insertStmt, session);
}
if (table instanceof OlapTable) {
OlapTable olapTable = (OlapTable) table;
List<Long> targetPartitionIds = Lists.newArrayList();
PartitionNames targetPartitionNames = insertStmt.getTargetPartitionNames();
if (insertStmt.isSpecifyPartitionNames()) {
if (targetPartitionNames.getPartitionNames().isEmpty()) {
throw new SemanticException("No partition specified in partition lists",
targetPartitionNames.getPos());
}
List<String> deduplicatePartitionNames =
targetPartitionNames.getPartitionNames().stream().distinct().collect(Collectors.toList());
if (deduplicatePartitionNames.size() != targetPartitionNames.getPartitionNames().size()) {
insertStmt.setTargetPartitionNames(new PartitionNames(targetPartitionNames.isTemp(),
deduplicatePartitionNames, targetPartitionNames.getPartitionColNames(),
targetPartitionNames.getPartitionColValues(), targetPartitionNames.getPos()));
}
for (String partitionName : deduplicatePartitionNames) {
if (Strings.isNullOrEmpty(partitionName)) {
throw new SemanticException("there are empty partition name", targetPartitionNames.getPos());
}
Partition partition = olapTable.getPartition(partitionName, targetPartitionNames.isTemp());
if (partition == null) {
throw new SemanticException("Unknown partition '%s' in table '%s'", partitionName,
olapTable.getName(), targetPartitionNames.getPos());
}
targetPartitionIds.add(partition.getId());
}
} else if (insertStmt.isStaticKeyPartitionInsert()) {
checkStaticKeyPartitionInsert(insertStmt, table, targetPartitionNames);
} else {
for (Partition partition : olapTable.getPartitions()) {
targetPartitionIds.add(partition.getId());
}
if (targetPartitionIds.isEmpty()) {
throw new SemanticException("data cannot be inserted into table with empty partition." +
"Use `SHOW PARTITIONS FROM %s` to see the currently partitions of this table. ",
olapTable.getName());
}
}
insertStmt.setTargetPartitionIds(targetPartitionIds);
}
if (table.isIcebergTable() || table.isHiveTable()) {
if (table.isHiveTable() && table.isUnPartitioned() &&
HiveWriteUtils.isS3Url(table.getTableLocation()) && insertStmt.isOverwrite()) {
throw new SemanticException("Unsupported insert overwrite hive unpartitioned table with s3 location");
}
if (table.isHiveTable() && ((HiveTable) table).getHiveTableType() != HiveTable.HiveTableType.MANAGED_TABLE &&
!session.getSessionVariable().enableWriteHiveExternalTable()) {
throw new SemanticException("Only support to write hive managed table, tableType: " +
((HiveTable) table).getHiveTableType());
}
PartitionNames targetPartitionNames = insertStmt.getTargetPartitionNames();
List<String> tablePartitionColumnNames = table.getPartitionColumnNames();
if (insertStmt.getTargetColumnNames() != null) {
for (String partitionColName : tablePartitionColumnNames) {
if (!insertStmt.getTargetColumnNames().contains(partitionColName)) {
throw new SemanticException("Must include partition column %s", partitionColName);
}
}
} else if (insertStmt.isStaticKeyPartitionInsert()) {
checkStaticKeyPartitionInsert(insertStmt, table, targetPartitionNames);
}
List<Column> partitionColumns = tablePartitionColumnNames.stream()
.map(table::getColumn)
.collect(Collectors.toList());
for (Column column : partitionColumns) {
if (isUnSupportedPartitionColumnType(column.getType())) {
throw new SemanticException("Unsupported partition column type [%s] for %s table sink",
column.getType().canonicalName(), table.getType());
}
}
}
List<Column> targetColumns;
Set<String> mentionedColumns = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
if (insertStmt.getTargetColumnNames() == null) {
if (table instanceof OlapTable) {
targetColumns = new ArrayList<>(((OlapTable) table).getBaseSchemaWithoutGeneratedColumn());
mentionedColumns =
((OlapTable) table).getBaseSchemaWithoutGeneratedColumn().stream()
.map(Column::getName).collect(Collectors.toSet());
} else {
targetColumns = new ArrayList<>(table.getBaseSchema());
mentionedColumns =
table.getBaseSchema().stream().map(Column::getName).collect(Collectors.toSet());
}
} else {
targetColumns = new ArrayList<>();
List<Column> autoIncrementKeyColumns = table.getBaseSchema().stream().filter(Column::isKey)
.filter(Column::isAutoIncrement).collect(Collectors.toList());
int numSpecifiedKeyColumns = autoIncrementKeyColumns.size();
for (String colName : insertStmt.getTargetColumnNames()) {
Column column = table.getColumn(colName);
if (column == null) {
throw new SemanticException("Unknown column '%s' in '%s'", colName, table.getName());
}
if (column.isGeneratedColumn()) {
throw new SemanticException("generated column '%s' can not be specified", colName);
}
if (!mentionedColumns.add(colName)) {
throw new SemanticException("Column '%s' specified twice", colName);
}
if (column.isKey() && !column.isAutoIncrement()) {
numSpecifiedKeyColumns++;
}
targetColumns.add(column);
}
if (table.isOlapTable()) {
OlapTable olapTable = (OlapTable) table;
if (olapTable.getKeysType().equals(KeysType.PRIMARY_KEYS)) {
if (numSpecifiedKeyColumns != olapTable.getKeysNum()) {
throw new SemanticException("should specify all key columns when insert to primary key table");
}
if (targetColumns.size() < olapTable.getBaseSchemaWithoutGeneratedColumn().size()) {
insertStmt.setUsePartialUpdate();
}
}
}
}
if (!insertStmt.usePartialUpdate()) {
for (Column column : table.getBaseSchema()) {
Column.DefaultValueType defaultValueType = column.getDefaultValueType();
if (defaultValueType == Column.DefaultValueType.NULL && !column.isAllowNull() &&
!column.isAutoIncrement() && !column.isGeneratedColumn() &&
!mentionedColumns.contains(column.getName())) {
StringBuilder msg = new StringBuilder();
for (String s : mentionedColumns) {
msg.append(" ").append(s).append(" ");
}
throw new SemanticException("'%s' must be explicitly mentioned in column permutation: %s",
column.getName(), msg.toString());
}
}
}
int mentionedColumnSize = mentionedColumns.size();
if ((table.isIcebergTable() || table.isHiveTable()) && insertStmt.isStaticKeyPartitionInsert()) {
mentionedColumnSize -= table.getPartitionColumnNames().size();
}
if (query.getRelationFields().size() != mentionedColumnSize) {
throw new SemanticException("Column count doesn't match value count");
}
if (query instanceof ValuesRelation) {
ValuesRelation valuesRelation = (ValuesRelation) query;
for (List<Expr> row : valuesRelation.getRows()) {
for (int columnIdx = 0; columnIdx < row.size(); ++columnIdx) {
Column column = targetColumns.get(columnIdx);
Column.DefaultValueType defaultValueType = column.getDefaultValueType();
if (row.get(columnIdx) instanceof DefaultValueExpr &&
defaultValueType == Column.DefaultValueType.NULL &&
!column.isAutoIncrement()) {
throw new SemanticException("Column has no default value, column=%s", column.getName());
}
AnalyzerUtils.verifyNoAggregateFunctions(row.get(columnIdx), "Values");
AnalyzerUtils.verifyNoWindowFunctions(row.get(columnIdx), "Values");
}
}
}
insertStmt.setTargetTable(table);
if (session.getDumpInfo() != null) {
session.getDumpInfo().addTable(insertStmt.getTableName().getDb(), table);
}
}

Line under review: throw new SemanticException("should specify all key columns when insert to primary key table");

Code after addressing the review:
public static void analyze(InsertStmt insertStmt, ConnectContext session) {
QueryRelation query = insertStmt.getQueryStatement().getQueryRelation();
new QueryAnalyzer(session).analyze(insertStmt.getQueryStatement());
List<Table> tables = new ArrayList<>();
AnalyzerUtils.collectSpecifyExternalTables(insertStmt.getQueryStatement(), tables, Table::isHiveTable);
tables.stream().map(table -> (HiveTable) table)
.forEach(table -> table.useMetadataCache(false));
/*
*  Target table
*/
Table table;
if (insertStmt.getTargetTable() != null) {
table = insertStmt.getTargetTable();
} else {
table = getTargetTable(insertStmt, session);
}
if (table instanceof OlapTable) {
OlapTable olapTable = (OlapTable) table;
List<Long> targetPartitionIds = Lists.newArrayList();
PartitionNames targetPartitionNames = insertStmt.getTargetPartitionNames();
if (insertStmt.isSpecifyPartitionNames()) {
if (targetPartitionNames.getPartitionNames().isEmpty()) {
throw new SemanticException("No partition specified in partition lists",
targetPartitionNames.getPos());
}
List<String> deduplicatePartitionNames =
targetPartitionNames.getPartitionNames().stream().distinct().collect(Collectors.toList());
if (deduplicatePartitionNames.size() != targetPartitionNames.getPartitionNames().size()) {
insertStmt.setTargetPartitionNames(new PartitionNames(targetPartitionNames.isTemp(),
deduplicatePartitionNames, targetPartitionNames.getPartitionColNames(),
targetPartitionNames.getPartitionColValues(), targetPartitionNames.getPos()));
}
for (String partitionName : deduplicatePartitionNames) {
if (Strings.isNullOrEmpty(partitionName)) {
throw new SemanticException("there are empty partition name", targetPartitionNames.getPos());
}
Partition partition = olapTable.getPartition(partitionName, targetPartitionNames.isTemp());
if (partition == null) {
throw new SemanticException("Unknown partition '%s' in table '%s'", partitionName,
olapTable.getName(), targetPartitionNames.getPos());
}
targetPartitionIds.add(partition.getId());
}
} else if (insertStmt.isStaticKeyPartitionInsert()) {
checkStaticKeyPartitionInsert(insertStmt, table, targetPartitionNames);
} else {
for (Partition partition : olapTable.getPartitions()) {
targetPartitionIds.add(partition.getId());
}
if (targetPartitionIds.isEmpty()) {
throw new SemanticException("data cannot be inserted into table with empty partition." +
"Use `SHOW PARTITIONS FROM %s` to see the currently partitions of this table. ",
olapTable.getName());
}
}
insertStmt.setTargetPartitionIds(targetPartitionIds);
}
if (table.isIcebergTable() || table.isHiveTable()) {
if (table.isHiveTable() && table.isUnPartitioned() &&
HiveWriteUtils.isS3Url(table.getTableLocation()) && insertStmt.isOverwrite()) {
throw new SemanticException("Unsupported insert overwrite hive unpartitioned table with s3 location");
}
if (table.isHiveTable() && ((HiveTable) table).getHiveTableType() != HiveTable.HiveTableType.MANAGED_TABLE &&
!session.getSessionVariable().enableWriteHiveExternalTable()) {
throw new SemanticException("Only support to write hive managed table, tableType: " +
((HiveTable) table).getHiveTableType());
}
PartitionNames targetPartitionNames = insertStmt.getTargetPartitionNames();
List<String> tablePartitionColumnNames = table.getPartitionColumnNames();
if (insertStmt.getTargetColumnNames() != null) {
for (String partitionColName : tablePartitionColumnNames) {
if (!insertStmt.getTargetColumnNames().contains(partitionColName)) {
throw new SemanticException("Must include partition column %s", partitionColName);
}
}
} else if (insertStmt.isStaticKeyPartitionInsert()) {
checkStaticKeyPartitionInsert(insertStmt, table, targetPartitionNames);
}
List<Column> partitionColumns = tablePartitionColumnNames.stream()
.map(table::getColumn)
.collect(Collectors.toList());
for (Column column : partitionColumns) {
if (isUnSupportedPartitionColumnType(column.getType())) {
throw new SemanticException("Unsupported partition column type [%s] for %s table sink",
column.getType().canonicalName(), table.getType());
}
}
}
List<Column> targetColumns;
Set<String> mentionedColumns = Sets.newTreeSet(String.CASE_INSENSITIVE_ORDER);
if (insertStmt.getTargetColumnNames() == null) {
if (table instanceof OlapTable) {
targetColumns = new ArrayList<>(((OlapTable) table).getBaseSchemaWithoutGeneratedColumn());
mentionedColumns =
((OlapTable) table).getBaseSchemaWithoutGeneratedColumn().stream()
.map(Column::getName).collect(Collectors.toSet());
} else {
targetColumns = new ArrayList<>(table.getBaseSchema());
mentionedColumns =
table.getBaseSchema().stream().map(Column::getName).collect(Collectors.toSet());
}
} else {
targetColumns = new ArrayList<>();
Set<String> requiredKeyColumns = table.getBaseSchema().stream().filter(Column::isKey)
.filter(c -> !c.isAutoIncrement()).map(c -> c.getName().toLowerCase()).collect(Collectors.toSet());
for (String colName : insertStmt.getTargetColumnNames()) {
Column column = table.getColumn(colName);
if (column == null) {
throw new SemanticException("Unknown column '%s' in '%s'", colName, table.getName());
}
if (column.isGeneratedColumn()) {
throw new SemanticException("generated column '%s' can not be specified", colName);
}
if (!mentionedColumns.add(colName)) {
throw new SemanticException("Column '%s' specified twice", colName);
}
requiredKeyColumns.remove(colName.toLowerCase());
targetColumns.add(column);
}
if (table.isOlapTable()) {
OlapTable olapTable = (OlapTable) table;
if (olapTable.getKeysType().equals(KeysType.PRIMARY_KEYS)) {
if (!requiredKeyColumns.isEmpty()) {
String missingKeyColumns = String.join(",", requiredKeyColumns);
ErrorReport.reportSemanticException(ErrorCode.ERR_MISSING_KEY_COLUMNS, missingKeyColumns);
}
if (targetColumns.size() < olapTable.getBaseSchemaWithoutGeneratedColumn().size()) {
insertStmt.setUsePartialUpdate();
}
}
}
}
if (!insertStmt.usePartialUpdate()) {
for (Column column : table.getBaseSchema()) {
Column.DefaultValueType defaultValueType = column.getDefaultValueType();
if (defaultValueType == Column.DefaultValueType.NULL && !column.isAllowNull() &&
!column.isAutoIncrement() && !column.isGeneratedColumn() &&
!mentionedColumns.contains(column.getName())) {
StringBuilder msg = new StringBuilder();
for (String s : mentionedColumns) {
msg.append(" ").append(s).append(" ");
}
throw new SemanticException("'%s' must be explicitly mentioned in column permutation: %s",
column.getName(), msg.toString());
}
}
}
int mentionedColumnSize = mentionedColumns.size();
if ((table.isIcebergTable() || table.isHiveTable()) && insertStmt.isStaticKeyPartitionInsert()) {
mentionedColumnSize -= table.getPartitionColumnNames().size();
}
if (query.getRelationFields().size() != mentionedColumnSize) {
throw new SemanticException("Column count doesn't match value count");
}
if (query instanceof ValuesRelation) {
ValuesRelation valuesRelation = (ValuesRelation) query;
for (List<Expr> row : valuesRelation.getRows()) {
for (int columnIdx = 0; columnIdx < row.size(); ++columnIdx) {
Column column = targetColumns.get(columnIdx);
Column.DefaultValueType defaultValueType = column.getDefaultValueType();
if (row.get(columnIdx) instanceof DefaultValueExpr &&
defaultValueType == Column.DefaultValueType.NULL &&
!column.isAutoIncrement()) {
throw new SemanticException("Column has no default value, column=%s", column.getName());
}
AnalyzerUtils.verifyNoAggregateFunctions(row.get(columnIdx), "Values");
AnalyzerUtils.verifyNoWindowFunctions(row.get(columnIdx), "Values");
}
}
}
insertStmt.setTargetTable(table);
if (session.getDumpInfo() != null) {
session.getDumpInfo().addTable(insertStmt.getTableName().getDb(), table);
}
}