Error when using JDBC to access dremio

Hi,

using the arrow flight sql JDBC driver yields mixed results for me. The crux of my issue seems to be that wile PreparedStatement.executeQuery() works fine, its sibling PreparedStatement.executeUpdate() doesn’t. The job appears in the Dremio UI, but then the job fails with a “(java.lang.IllegalStateException) Tenant context should not be null” from the Nessie layers. Same JDBC connection, same SQL query (an INSERT INTO in this case).

Is there something special to put in the JDBC URL or in the connection settings to get this to work?

Hi @dremmer ,

Can you clarify the Dremio version and arrow flight JDBC driver?

dremio cloud (so no idea about the server version), and driver 18.3.0

Can you get us a small java snippet that triggers the issue? I tried with the following and it didn’t seem to reproduce:

    // Step 1: CREATE TABLE using executeUpdate()
    // This also triggers the "Tenant context should not be null" error from Nessie layers
    String createTableQuery = "CREATE TABLE IF NOT EXISTS " + FULL_SCHEMA_PATH + ".test_table (id INT, name VARCHAR)";

    try (PreparedStatement pstmt = conn.prepareStatement(createTableQuery)) {
        System.out.println("Executing CREATE TABLE: " + createTableQuery);
        int result = pstmt.executeUpdate();
        System.out.println("[SUCCESS] CREATE TABLE succeeded with result: " + result);
    } catch (SQLException e) {
        System.err.println("[EXPECTED ERROR] CREATE TABLE failed:");
        printSqlException(e);

        if (containsTenantContextError(e)) {
            System.out.println();
            System.out.println(">>> CONFIRMED: This is the 'Tenant context should not be null' error <<<");
            System.out.println(">>> from the Nessie layers as described in the forum post.           <<<");
        }
        // Return early since INSERT will also fail if CREATE TABLE failed
        System.out.println();
        return;
    }
    System.out.println();

    // Step 2: INSERT using executeUpdate()
    // This also triggers the "Tenant context should not be null" error from Nessie layers
    String insertQuery = "INSERT INTO " + FULL_SCHEMA_PATH + ".test_table (id, name) VALUES (1, 'test')";

    try (PreparedStatement pstmt = conn.prepareStatement(insertQuery)) {
        System.out.println("Executing INSERT: " + insertQuery);
        int result = pstmt.executeUpdate();
        System.out.println("[SUCCESS] INSERT succeeded with result: " + result);
    } catch (SQLException e) {
        System.err.println("[EXPECTED ERROR] INSERT failed:");
        printSqlException(e);

        // Check if it's the specific error from the forum post
        if (containsTenantContextError(e)) {
            System.out.println();
            System.out.println(">>> CONFIRMED: This is the 'Tenant context should not be null' error <<<");
            System.out.println(">>> from the Nessie layers as described in the forum post.           <<<");
        }
        // Return early since INSERT INTO SELECT will also fail
        System.out.println();
        return;
    }

indeed, “CREATE TABLE…” statements work. In my case it was a “INSERT INTO…” to a table in an Arctic catalog. My java class is

package test.dremio;

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

public final class DremioCli {

    private enum Mode {
        REGULAR,
        PREPARED_EX,
        PREPARED_UP
    }

    private DremioCli() {
    }

    public static void main(String[] args) throws Exception {
        String host = null, port = null, token = null, catalog = null, query = null;
        Mode mode = Mode.REGULAR;
        for (String arg : args) {
            if (arg.startsWith("--mode=")) {
                mode = Mode.valueOf(arg.substring("--mode=".length()).toUpperCase());
            }
            if (arg.startsWith("--host=")) {
                host = arg.substring("--host=".length());
            }
            if (arg.startsWith("--port=")) {
                port = arg.substring("--port=".length());
            }
            if (arg.startsWith("--token=")) {
                token = arg.substring("--token=".length());
            }
            if (arg.startsWith("--catalog=")) {
                catalog = arg.substring("--catalog=".length());
            }
            if (arg.startsWith("--query=")) {
                query = arg.substring("--query=".length());
            }
        }

        String jdbcUrl = buildJdbcUrl(host, port, token, catalog);

        System.out.println("Run with");
        System.out.println("JDBC URL: " + jdbcUrl);
        System.out.println("Mode: " + mode);
        System.out.println("Query: " + query);

        Class.forName("org.apache.arrow.driver.jdbc.ArrowFlightJdbcDriver");

        Connection connection = DriverManager.getConnection(jdbcUrl);
        if (mode == Mode.REGULAR) {
            try (Statement statement = connection.createStatement()) {
                statement.execute(query);
            }
        } else if (mode == Mode.PREPARED_EX) {
            try (PreparedStatement statement = connection.prepareStatement(query)) {
                statement.execute();
            }
        } else if (mode == Mode.PREPARED_UP) {
            try (PreparedStatement statement = connection.prepareStatement(query)) {
                statement.executeUpdate();
            }
        }
        try {
            connection.close();
        } catch (Exception e) {
            System.err.println("Failed to close connection: " + e.getMessage());
        }
    }

    private static String buildJdbcUrl(String host, String port, String token, String catalog) {
        return "jdbc:arrow-flight-sql://"
                + host
                + ":"
                + port
                + "/?token="
                + urlEncode(token)
                + "&catalog="
                + urlEncode(catalog);
    }

    private static String urlEncode(String value) {
        return java.net.URLEncoder.encode(value, java.nio.charset.StandardCharsets.UTF_8);
    }
}

=> it works when I use execute() on prepared statements, but not regular statements nor executeUpdate() on preparedStatements