C:/test outside C:/

612 views
Skip to first unread message

István Bartók

unread,
May 13, 2014, 3:40:49 PM5/13/14
to h2-da...@googlegroups.com
Hi,

I get an exception with a strange "C:/test outside C:/" message, when:
- Opening a database with implicit relative path name
- While h2.baseDir is set to the root of a Windows drive ("C:/" or "C:\\")

However, it works as expected with h2.baseDir="C:/anything".

Example code:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class H2BaseDirProblem
{
    public static void main(String[] args)
    throws SQLException
    {
        Connection conn;

        // Works:

        System.setProperty("h2.baseDir", "C:/anything");
        conn = DriverManager.getConnection("jdbc:h2:test", "sa", "");

        // Fails with any of these:

        System.setProperty("h2.baseDir", "C:/");
        conn = DriverManager.getConnection("jdbc:h2:test", "sa", "");

        System.setProperty("h2.baseDir", "C:\\");
        conn = DriverManager.getConnection("jdbc:h2:test", "sa", "");

        conn.close();
    }
}

It throws the following exception:

Exception in thread "main" org.h2.jdbc.JdbcSQLException: IO Exception: "C:/test outside C:/" [90028-174]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:332)
    at org.h2.message.DbException.get(DbException.java:172)
    at org.h2.message.DbException.get(DbException.java:149)
    at org.h2.engine.ConnectionInfo.setBaseDir(ConnectionInfo.java:192)
    at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:101)
    at org.h2.jdbc.JdbcConnection.<init>(JdbcConnection.java:90)
    at org.h2.Driver.connect(Driver.java:73)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at java.sql.DriverManager.getConnection(Unknown Source)
    at H2BaseDirProblem.main(H2BaseDirProblem.java:20)


Is this the expected behaviour?
Based on the strange error message (well, C:/test is not outside C:/) I have a feeling that an internal check doesn't work the way it was originally intended.

I am using h2-1.3.174.jar on Windows7, JRE6.

Hint to localize the problem:

From the exception message I guess the value of absDir was "C:/".
I guess the trailing '/' is unexpected in the condition check in the following statement:

org.h2.engine.ConnectionInfo.setBaseDir(ConnectionInfo.java:188)

            if (normalizedName.charAt(absDir.length()) != '/') {
                // database must be within the directory
                // (with baseDir=/test, the database name must not be
                // /test2/x and not /test2)
                throw DbException.get(ErrorCode.IO_EXCEPTION_1, normalizedName + " outside " +
                        absDir);
            }


A different, maybe related case:

It also throws an exception with h2.baseDir="C:" (without slash or backslash).
The message in that case is: "C:/test outside C:/<current/working/directory/on/C>".

The error message is correct by itself (it is actually outside).
However, I do not understand why the relative DB path was interpreted literally at "C:", while the baseDir became " C:/<current/working/directory/on/C>".

Is that the intended behaviour?

ps: The case with "C:" is not really important, just noted - my real trouble was with "C:/"

Thanks,
--
Bartók István

Thomas Mueller

unread,
May 14, 2014, 1:44:15 AM5/14/14
to h2-da...@googlegroups.com
Hi,

Yes, this is a bug. I have a patch:

Index: src/main/org/h2/engine/ConnectionInfo.java
===================================================================
--- src/main/org/h2/engine/ConnectionInfo.java (revision 5657)
+++ src/main/org/h2/engine/ConnectionInfo.java (working copy)
@@ -183,7 +183,9 @@
                 throw DbException.get(ErrorCode.IO_EXCEPTION_1, normalizedName + " outside " +
                         absDir);
             }
-            if (normalizedName.charAt(absDir.length()) != '/') {
+            if (absDir.endsWith("/") || absDir.endsWith("\\")) {
+                // no further checks are needed for C:/ and similar
+            } else if (normalizedName.charAt(absDir.length()) != '/') {
                 // database must be within the directory
                 // (with baseDir=/test, the database name must not be
                 // /test2/x and not /test2)

The "C:" case I like to not support, as it means the current working directory for C:, which is unexpected for many people.

By the way, for H2 version 1.4.x, the database URL would need to be "jdbc:h2:./test" (explicitly state that the database is relative to another directory).

Regards,
Thomas
--
You received this message because you are subscribed to the Google Groups "H2 Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email to h2-database...@googlegroups.com.
To post to this group, send email to h2-da...@googlegroups.com.
Visit this group at http://groups.google.com/group/h2-database.
For more options, visit https://groups.google.com/d/optout.

István Bartók

unread,
May 14, 2014, 6:59:36 AM5/14/14
to h2-da...@googlegroups.com
Hi Thomas,

Thank you for the quick response and the patch!
Is this a one-off fix, or do you plan to include it into future versions?

Thanks,
--
Bartók István
To unsubscribe from this group and stop receiving emails from it, send an email to h2-database+unsubscribe@googlegroups.com.

christof...@finaris.de

unread,
May 14, 2014, 8:58:08 AM5/14/14
to h2-da...@googlegroups.com
Hi,

Our application uses an embedded H2 database (1.3.173). During execution, table functions are created. Some of them execute select-statements in the H2 database.
When not running in in multi-threaded mode, and if multiple threads (each with separate connection) are accessing the same table function, then deadlocks can occur.

This seems to be due to the fact, that the final statement execution inside H2 is synchronized over the database, and The statement preparation is synchronized over the TableView object:

start execution
--prepare statement (synchronized over a TableView)
----query table function
------prepare statement (synchronized over a TableView)
------execute (synchronized over database)
--End of outer prepare
--start execution(synchronized over database)
----query table function
------prepare statement (synchronized over a TableView)
------execute (synchronized over database)

As you can see in this execution flow, due to the fact that the table function executes a query to H2, sometimes TableView synchronization comes before database synchronization, and sometimes it's the other way around.
As consequence, if there a multiple parallel threads doing this, deadlocks can occur.

My current work-around is a synchronization of H2 query execution over a singleton instance, if no multithreading is used.

I know that this usage of H2 is quite special, but perhaps there is a possibility to fix this? Or would this be out of scope of H2?


Kind regards

Christoff Schmitz

F I N A R I S
Financial Software Partner GmbH
Sömmerringstrasse 23
60322 Frankfurt am Main

Fon:      +49 (0)69  / 254 98 - 24
Mobile: +49 (0)176 / 206 34 186
Fax:       +49 (0)69  / 254 98 - 50
eMail:    
mailto:Christof...@finaris.de
www:      
http://www.finaris.de und http://www.rapidrep.com

To unsubscribe from this group and stop receiving emails from it, send an email to h2-database...@googlegroups.com.


To post to this group, send email to h2-da...@googlegroups.com.
Visit this group at

http://groups.google.com/group/h2-database.
For more options, visit
https://groups.google.com/d/optout.

--

You received this message because you are subscribed to the Google Groups "H2 Database" group.

To unsubscribe from this group and stop receiving emails from it, send an email to h2-database...@googlegroups.com.


To post to this group, send email to

h2-da...@googlegroups.com.
Visit this group at
http://groups.google.com/group/h2-database.
For more options, visit
https://groups.google.com/d/optout.


================================================================================================================
Disclaimer
The information contained in this e - mail and any attachments ( together the "message") is intended for the addressee only and
may contain confidential and/or privileged information. If you have received the message by mistake please delete it and notify
the sender and do not copy or distribute it or disclose its contents to anyone.

FINARIS Financial Software Partner GmbH, Sömmerringstr. 23, 60322 Frankfurt/Main, Germany
Registered at Frankfurt/Main, HRB 52873, Managing Directors: Dipl. Inf. Hermann Friebel, Dipl. Ing. Kai Bächle, Dipl. Inf. Werner Märkl
================================================================================================================

Noel Grandin

unread,
May 14, 2014, 9:09:32 AM5/14/14
to h2-da...@googlegroups.com

That is a classic example of an ABBA lock, and we are happy to track them down and fix them.

Can you identify for us a stack-trace where it synchronizes over the database while holding a lock on a TableView?

If you are using the Eclipse debugger, this is very easy - when it is paused, right-click on the thread and select Copy
Stack.
If you are not, then something like
new Throwable().printStackTrace(System.out);
in an appropriate place normally does the trick.

Thanks.

christof...@finaris.de

unread,
May 14, 2014, 10:09:33 AM5/14/14
to h2-da...@googlegroups.com
Hi,

see the stack trace below, I marked the positions where synchronization occurs over a TableView (red)  or Database (blue).

The root cause is, as explained, that the table function implementation within my class (DataElementTableFunctionManager) triggers a new query execution to H2, some sort of statement execution within statement execution (or within statement parsing as in the stack trace).
Maybe this is not recommended to do, but in my case this should not be a problem, since the data read by the table function never updated within H2.

Thread [#4 (working)] (Suspended)        
        CommandContainer(Command).executeQuery(int, boolean) line: 190        (SYNC: Database)
        JdbcStatement.executeQuery(String) line: 78        
        DataElementTableFunctionManager.readTableFunction(String, String, String, int, int, String) line: 170        (not part of H2)
        H2DataElementTableFunctionManager.readTableFunction(Connection, String, String, int, int, String) line: 98        (not part of H2)
        GeneratedMethodAccessor50.invoke(Object, Object[]) line: not available        
        DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available        
        Method.invoke(Object, Object...) line: not available        
        FunctionAlias$JavaMethod.getValue(Session, Expression[], boolean) line: 411        
        JavaFunction.getValueForColumnList(Session, Expression[]) line: 129        
        FunctionTable.<init>(Schema, Session, Expression, FunctionCall) line: 63        
        Parser.readTableFilter(boolean) line: 1100        
        Parser.parseSelectSimpleFromPart(Select) line: 1713        
        Parser.parseSelectSimple() line: 1821        
        Parser.parseSelectSub() line: 1707        
        Parser.parseSelectUnion() line: 1550        
        Parser.parseSelect() line: 1538        
        Parser.parsePrepared() line: 405        
        Parser.parse(String, boolean) line: 279        
        Parser.parse(String) line: 251        
        Parser.prepare(String) line: 202        
        Session.prepare(String, boolean) line: 401        
        ViewIndex.getQuery(Session, int[]) line: 268        
        ViewIndex.<init>(TableView, ViewIndex, Session, int[]) line: 71        
        TableView.getBestPlanItem(Session, int[], SortOrder) line: 212        (SYNC: TableView)
        TableView.getScanIndex(Session) line: 377        
        TableFilter.getBestPlanItem(Session, int) line: 163        
        Plan.calculateCost(Session) line: 111        
        Optimizer.testPlan(TableFilter[]) line: 177        
        Optimizer.calculateBestPlan() line: 81        
        Optimizer.optimize() line: 230        
        Select.preparePlan() line: 931        
        Select.prepare() line: 832        
        Parser.prepare(String) line: 203        
        Session.prepare(String, boolean) line: 401        
        Session.prepare(String) line: 388        
        TableView.compileViewQuery(Session, String) line: 101        
        TableView.initColumnsAndTables(Session) line: 146        
        TableView.init(String, ArrayList<Parameter>, String[], Session, boolean) line: 97        
        TableView.<init>(Schema, int, String, String, ArrayList<Parameter>, String[], Session, boolean) line: 63        
        TableView.createTempView(Session, User, String, Query, Query) line: 451        
        Parser.readTableFilter(boolean) line: 1053        
        Parser.parseSelectSimpleFromPart(Select) line: 1713        
        Parser.parseSelectSimple() line: 1821        
        Parser.parseSelectSub() line: 1707        
        Parser.parseSelectUnion() line: 1550        
        Parser.parseSelect() line: 1538        
        Parser.parsePrepared() line: 405        
        Parser.parse(String, boolean) line: 279        
        Parser.parse(String) line: 251        
        Parser.prepareCommand(String) line: 218        
        Session.prepareLocal(String) line: 428        
        Session.prepareCommand(String, int) line: 377        
        JdbcConnection.prepareCommand(String, int) line: 1138        
        JdbcStatement.executeQuery(String) line: 72        
        [...]

Kind regards

Christoff Schmitz

F I N A R I S
Financial Software Partner GmbH
Sömmerringstrasse 23
60322 Frankfurt am Main

Fon:      +49 (0)69  / 254 98 - 24
Mobile: +49 (0)176 / 206 34 186
Fax:       +49 (0)69  / 254 98 - 50
eMail:    
mailto:Christof...@finaris.de
www:      
http://www.finaris.de und http://www.rapidrep.com



--
You received this message because you are subscribed to the Google Groups "H2 Database" group.
To unsubscribe from this group and stop receiving emails from it, send an email to h2-database...@googlegroups.com.
To post to this group, send email to h2-da...@googlegroups.com.
Visit this group at
http://groups.google.com/group/h2-database.
For more options, visit
https://groups.google.com/d/optout.

Noel Grandin

unread,
May 14, 2014, 10:48:55 AM5/14/14
to h2-da...@googlegroups.com
Hi

Thanks for the stacktrace.

I think I've fixed this in SVN.

You could either build H2 yourself, or wait until tomorrow and try out the daily build.

Regards, Noel

Thomas Mueller

unread,
May 17, 2014, 2:33:31 PM5/17/14
to H2 Google Group
Hi,

This will be included in future versions of course :-)

Regards,
Thomas



To unsubscribe from this group and stop receiving emails from it, send an email to h2-database...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages