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

Unexpected token ON CONFLICT for Postgresql #11

Open
trungdoviet opened this issue Jul 31, 2024 · 0 comments
Open

Unexpected token ON CONFLICT for Postgresql #11

trungdoviet opened this issue Jul 31, 2024 · 0 comments

Comments

@trungdoviet
Copy link

hi team
im using mybatisplus for postgre sql and using plugins like tenantInteceptor, Audit Interceptor,...
those plugin use jsql parser to parse SQL syntax. but for my query:

INSERT INTO the_table (id, column_1, column_2) 
VALUES (1, 'A', 'X'), (2, 'B', 'Y'), (3, 'C', 'Z')
ON CONFLICT (id) DO UPDATE 
  SET column_1 = excluded.column_1, 
      column_2 = excluded.column_2;

it cause exception:
Caused by: net.sf.jsqlparser.parser.ParseException: Encountered unexpected token: "CONFLICT" <S_IDENTIFIER> at line 202, column 14. Was expecting: "DUPLICATE", at net.sf.jsqlparser.parser.CCJSqlParser.generateParseException(CCJSqlParser.java:26538), at net.sf.jsqlparser.parser.CCJSqlParser.jj_consume_token(CCJSqlParser.java:26377), at net.sf.jsqlparser.parser.CCJSqlParser.Insert(CCJSqlParser.java:2155), at net.sf.jsqlparser.parser.CCJSqlParser.SingleStatement(CCJSqlParser.java:143), at net.sf.jsqlparser.parser.CCJSqlParser.Statements(CCJSqlParser.java:502), at net.sf.jsqlparser.parser.CCJSqlParserUtil.parseStatements(CCJSqlParserUtil.java:186), at net.sf.jsqlparser.parser.CCJSqlParserUtil.parseStatements(CCJSqlParserUtil.java:176), at com.baomidou.mybatisplus.extension.parser.JsqlParserSupport.parserMulti(JsqlParserSupport.java:63), ... 106 common frames omitted

here is my audit interceptor:

@RequiredArgsConstructor
public class AuditInterceptor extends JsqlParserSupport implements InnerInterceptor {

    @Override
    public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
        MPStatementHandler mpStatementHandler = PluginUtils.mpStatementHandler(sh);
        MappedStatement mappedStatement = mpStatementHandler.mappedStatement();
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        if (sqlCommandType == SqlCommandType.UPDATE || sqlCommandType == SqlCommandType.INSERT) {
            MPBoundSql mpBoundSql = mpStatementHandler.mPBoundSql();
            Class<?> entityType = mappedStatement.getParameterMap().getType();
            if (!mpBoundSql.sql().contains("REFRESH MATERIALIZED VIEW")) {
                mpBoundSql.sql(parserMulti(mpBoundSql.sql(), entityType));
            }
        }
    }

    @Override
    protected void processInsert(Insert insert, int index, String sql, Object obj) {
        Set<String> columnNames = getColumnNames(obj);
        if (CollectionUtils.isEmpty(columnNames)) {
            return;
        }
        // Set update time & tenant ID
        var now = new LongValue(System.currentTimeMillis());
        setInsertValue(insert, columnNames, AuditEntity.CREATED_COLUMN_NAME, now);
        setInsertValue(insert, columnNames, AuditEntity.MODIFIED_COLUMN_NAME, now);
        setInsertValue(insert, columnNames, AuditEntity.TENANT_ID_COLUMN_NAME, new StringValue(ContextUtils.getOrThrowTenantId()));
        setInsertValue(insert, columnNames, AuditEntity.ENVIRONMENT_COLUMN_NAME, new StringValue(ContextUtils.getEnvName()));
    }

    private Set<String> getColumnNames(Object obj) {
        TableInfo tableInfo = TableInfoHelper.getTableInfo((Class<?>) obj);
        if (tableInfo == null) {
            getLog().warn("Cannot find table info for class '{}'", obj);
            return Collections.emptySet();
        }
        return tableInfo.getFieldList().stream()
            .map(TableFieldInfo::getColumn)
            .collect(Collectors.toSet());
    }

    private LogTagHolder getLog() {
        return MosLog.wrap(log).tag1("AUDIT_INTERCEPTOR");
    }

    private void setInsertValue(Insert insert, Set<String> definedColumnNames, String columnName, Expression columnValue) {
        List<Column> columns = insert.getColumns();
        if (!isAccept(columnName, definedColumnNames, columns)) {
            return;
        }
        columns.add(new Column(columnName));
        ItemsList itemsList = insert.getItemsList();
        if (itemsList instanceof MultiExpressionList) {
            ((MultiExpressionList) itemsList).getExpressionLists().forEach(el -> el.getExpressions().add(columnValue));
        } else if (itemsList instanceof ExpressionList) {
            ((ExpressionList) insert.getItemsList()).getExpressions().add(columnValue);
        }
        // TODO remove after check
        MosLog.wrap(log).onPre().tag1("TAN_TEST").tag2("INSERT")
            .info("[{}][{}] Set insert value", insert.getTable().getName(), columnName);
    }

    /**
     * Check if column exist in table definition & not exist in INSERT/UPDATE command.
     */
    private boolean isAccept(String columnName, Set<String> definedColumnNames, List<Column> insertColumns) {
        if (!definedColumnNames.contains(columnName)) {
            return false;
        }
        Column insertColumn = insertColumns.stream()
            .filter(curInsertColumn -> StringUtils.equals(columnName, curInsertColumn.getColumnName())
                || StringUtils.equals("`" + columnName + "`", curInsertColumn.getColumnName()))
            .findFirst()
            .orElse(null);
        return (insertColumn == null);
    }

    @Override
    protected void processUpdate(Update update, int index, String sql, Object obj) {
        Set<String> columnNames = getColumnNames(obj);
        if (CollectionUtils.isEmpty(columnNames)) {
            return;
        }
        setUpdateValue(update, columnNames, AuditEntity.MODIFIED_COLUMN_NAME, new LongValue(System.currentTimeMillis()));
    }

    private void setUpdateValue(Update update, Set<String> definedColumnNames, String columnName, Expression columnValue) {
        List<Column> columns = update.getColumns();
        if (!isAccept(columnName, definedColumnNames, columns)) {
            return;
        }
        columns.add(new Column(columnName));
        List<Expression> expressions = update.getExpressions();
        expressions.add(columnValue);
        // TODO remove after check
        MosLog.wrap(log).onPre().tag1("TAN_TEST").tag2("UPDATE")
            .info("[{}][{}] Set update value", update.getTable().getName(), columnName);
    }

}

for now im using mybatis-plus:

<mybatis-plus.version>3.4.3.1</mybatis-plus.version>
		<mybatis-plus-join.version>1.2.1</mybatis-plus-join.version>
<dependency>
				<groupId>com.baomidou</groupId>
				<artifactId>mybatis-plus</artifactId>
				<version>${mybatis-plus.version}</version>
			</dependency>
			<dependency>
				<groupId>com.baomidou</groupId>
				<artifactId>mybatis-plus-boot-starter</artifactId>
				<version>${mybatis-plus.version}</version>
			</dependency>
			<dependency>
				<groupId>com.github.yulichang</groupId>
				<artifactId>mybatis-plus-join</artifactId>
				<version>${mybatis-plus-join.version}</version>
			</dependency>

please guide how to fix this? look like that lib only support MySQL syntax

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant