简体   繁体   中英

Oracle and H2 Table with Keyword in Column

I have a Oracle table, say, mytbl , which has a column, OFFSET . I am trying to create unit tests using H2 and because OFFSET is a keyword for H2 , I am having to re-write the queries using a double quoted, "OFFSET" , instead of simply referring to the column as offset . I am using SpringFramework's EmbeddedDatabaseBuilder to spinup the H2. When instantiating the database for unit tests, I use the specification, NON_KEYWORDS=OFFSET , in the url. This specification is honored during instantiation, that is, I can refer to the column simply as offset . But the NON_KEYWORDS spec is ignored when the actual queries are invoked.

FWIW:

  • versions: H2:2.1.x, Oracle:19.4, Java:11, jUnit:4, Spring:5.3.x.
  • I am not using JPA or Hibernate.
  • SO references: ref1 , ref2

Here is the test class (the actual tests are irrelevant):

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;

public class MyClassDaoTest {
  private EmbeddedDatabase ds;
  private MyClassDao myClassDao;

  @Before
  public void setup() {
    this.ds = new EmbeddedDatabaseBuilder()
                  .setType( EmbeddedDatabaseType.H2 )
                  .setName( "dummy;MODE=Oracle;DATABASE_TO_UPPER=true;NON_KEYWORDS=OFFSET" )
                  .addScript( "/initialize-mytbl.sql" )
                  .build();
    this.myClassDao = new MyClassDao( new JdbcTemplate( this.ds ) );
  }

  @After
  public void shutdown() {
    this.ds.shutdown();
  }
}

Here is the loading script (note: I do not have to use "OFFSET" here), initialize-mytbl.sql :

CREATE TABLE MYTBL ( offset INTEGER NOT NULL );
INSERT INTO MYTBL ( offset ) VALUES (1);

And here is the actual query:

import org.springframework.jdbc.core.JdbcOperations;

public class MyClassDao {
  private final JdbcOperation j;
  public MyClassDao( JdbcOperations j ) { this.j = j; }
  public int fetchOffset() {
    // this fails in H2 but works in Oracle:
    //return j.queryForObject( "select offset from mytbl", Integer.class );
    // this works in both H2 and Oracle:
    return j.queryForObject( "select \"OFFSET\" from mytbl", Integer.class );
  }
}

So, my question is: why is the NON_KEYWORDS spec in the url being ignored in the actual queries? Obviously, I do not prefer to re-write all the queries replacing offset with "OFFSET" . And, more obviously, I can not alter the Oracle table, mytbl .

The actual exception thrown is:

Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "SELECT offset[*] from mytbl" ....

Few database systems such as PostgreSQL and H2 allow empty lists of select expressions, so it is possible to execute SELECT OFFSET 0 ROWS etc. in them. NON_KEYWORDS setting in H2 only works well when specified keywords don't conflict with any other grammar constructions, parser isn't that smart to see FROM after OFFSET when it determines whether OFFSET a keyword or an identifier.

Modern versions of Oracle also support standard OFFSET / FETCH clauses, but empty lists of select expressions are invalid, Oracle doesn't expect OFFSET / FETCH clauses here, it expects only an expression and its parser also uses additional quirks to distinguish the OFFSET clause from OFFSET identifier in other contexts, such as aliases ( SELECT * FROM TEST OFFSET ).

If you really need to use both database systems, OFFSET needs to be quoted in queries, currently there are no other solutions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM