Spring Boot data.sql初始化H2数据库时存在反斜杠后跟单引号的问题。

huangapple go评论112阅读模式
英文:

Spring Boot data.sql initialization of H2 database has issue with backslash followed by single quote

问题

在某些Spring Boot应用程序中,我使用data.sql文件初始化内存中的H2数据库,其中包含一些插入数据。数据库中的一个表被填充了包含字符串列的行,每个行中的字符串都以反斜杠结尾,例如'\1\'

由于反斜杠不被视为转义字符,因此插入'\1'会将此字符串文字地插入数据库(无需双反斜杠)。但是,在单引号之前的反斜杠似乎转义了该单引号,因此插入'\1\'会引发与语法相关的异常。如果我插入'\1\\',那么该字符串会被文字地放入数据库中(以两个反斜杠结尾)。

在与H2字符串数据类型相关的官方文档中,我找不到任何相关信息。我尝试在字符串前面加上U&以使用Unicode字符,但这也会引发错误。到目前为止,我找到的唯一解决方法是在最后一个反斜杠后添加一个空格,并在插入后执行更新(使用TRIM函数)以删除这些空格。

这个行为是否有解释,或者是否有更好的解决方法来避免这个问题?


最小示例:

设置:Spring Boot测试套件,包括以下的schema.sqldata.sql

schema.sql

CREATE SCHEMA IF NOT EXISTS TEST;

CREATE TABLE TEST.ONE (
    IDENTIFIER BIGINT AUTO_INCREMENT PRIMARY KEY,
    NAME VARCHAR(500)
);

CREATE TABLE TEST.TWO (
    IDENTIFIER BIGINT AUTO_INCREMENT PRIMARY KEY,
    NAME VARCHAR(500)
);

data.sql

INSERT INTO TEST.ONE(IDENTIFIER,NAME) VALUES
    (1,'\1\3\');

INSERT INTO TEST.TWO(IDENTIFIER,NAME) VALUES
    (1,'test;test');

我在application.properties中有以下配置:

server.port=8083
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb;INIT=CREATE SCHEMA IF NOT EXISTS TEST
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.data=classpath:data-test.sql
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.javax.persistence.schema-generation.drop-source=script
spring.jpa.properties.javax.persistence.schema-generation.drop-script-source=drop-tables.sql
spring.jpa.properties.hibernate.default_schema=TEST
spring.cache.jcache.config=classpath:ehcache.xml
spring.h2.console.enabled=true
spring.application.admin.jmx-name=org.springframework.boot:type=Admin,name=SpringApplication${random.int(1000000)}
  • 期望的行为:脚本运行正常
  • 实际行为:脚本运行失败,因为第二个插入的元组中的分号被解释为插入语句的结尾。如果在第一个插入的元组中反斜杠后面添加一个空格,脚本就会运行。如果更改插入的顺序,它也会运行。

H2驱动程序版本为1.4.200

英文:

In some Spring Boot app I initialize my in-memory H2 DB with some inserts (in the data.sql file). One of the tables in the DB is filled with rows where each row has a string column, and every string in this column ends with a backslash e.g. '\1\'.

The backslashes are not treated as an escape character hence inserting '\1' inserts this string literally (no need for double backslashes). However, the backslash before the single quote seems to escape that single quote, as inserting '\1\' causes a syntax-related exception. If I insert '\1\\' then that string is put in the DB literally (with the two backslashes at the end).

In the H2 docs relating to the string data type I can't find any information on this. I tried prefixing my strings with U& to use unicode chars but this throws an error as well. The only fix I found so far is to add a space after the last backslash and do an update after insertion (using the TRIM function) to remove said spaces.

Is this behaviour explained anywhere, or is there a better way to circumvent the issue?


Minimal example :

Setup : Spring Boot test suite with schema.sql and data.sql given below :

schema.sql

CREATE SCHEMA IF NOT EXISTS TEST;

CREATE TABLE TEST.ONE (
    IDENTIFIER BIGINT AUTO_INCREMENT PRIMARY KEY,
    NAME VARCHAR(500)
);

CREATE TABLE TEST.TWO (
    IDENTIFIER BIGINT AUTO_INCREMENT PRIMARY KEY,
    NAME VARCHAR(500)
);

data.sql

INSERT INTO TEST.ONE(IDENTIFIER,NAME) VALUES
    (1,'\');

INSERT INTO TEST.TWO(IDENTIFIER,NAME) VALUES
    (1,'test;test');

I have the following in my application.properties :

server.port=8083
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:testdb;INIT=CREATE SCHEMA IF NOT EXISTS TEST
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.data=classpath:data-test.sql
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.javax.persistence.schema-generation.drop-source=script
spring.jpa.properties.javax.persistence.schema-generation.drop-script-source=drop-tables.sql
spring.jpa.properties.hibernate.default_schema=TEST
spring.cache.jcache.config=classpath:ehcache.xml
spring.h2.console.enabled=true
spring.application.admin.jmx-name=org.springframework.boot:type=Admin,name=SpringApplication${random.int(1000000)}
  • Expected behaviour : script runs fine
  • Actual behaviour : script fails
    to run, because the semicolon in the second inserted tuple is
    interpreted as the end of the insert statement. If you add a space
    after the backslash in the first inserted tuple, the script does run.
    If you reverse the order of the inserts, it also runs.

H2 driver version 1.4.200

答案1

得分: 0

问题是由Spring的ScriptUtils类在其splitSQLScript方法中引起的。这是一个将data.sql脚本拆分为查询的方法,分号是默认分隔符。在扫描脚本时,它会跟踪是否位于由单引号或双引号括起的语句内。当它遇到反斜杠时,它会进入一种转义模式。反斜杠后面的字符以及反斜杠本身保持不变,但如果反斜杠后面的字符是单引号或双引号,它将被视为已经被转义。

解决这个问题的一种方法是使用Unicode:

U&'|005c' UESCAPE '|'

我无法仅仅使用U&'\1\005c' 使其工作。

相关的Spring问题报告可以在<https://github.com/spring-projects/spring-framework/issues/30098>找到。

英文:

The issue was caused by Spring's ScriptUtils class in its splitSQLScript method. It's a method that splits the data.sql script into queries with the semicolon as the default delimiter. Whilst scanning the script, it keeps track of whether or not it's within a statement surrounded by single or double quotes. When it encounters a backslash, it enters a sort of escape modus. The character after this backslash as well as the backslash itself are left as-is, but if the character after the backslash is a single or double quote it's effectively treated as if that quote were to be escaped.

One way to circumvent this is to use unicode :

U&amp;&#39;|005c&#39; UESCAPE &#39;|&#39;

I wasn't able to make this work with just U&amp;&#39;\1\005c&#39;.

The related Spring issue report can be found at <https://github.com/spring-projects/spring-framework/issues/30098>.

huangapple
  • 本文由 发表于 2023年3月8日 19:22:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75672375.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定