英文:
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.sql
和data.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&'|005c' UESCAPE '|'
I wasn't able to make this work with just U&'\1\005c'
.
The related Spring issue report can be found at <https://github.com/spring-projects/spring-framework/issues/30098>.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论