Skip to content

[BUG] PreparedStatement swallows exceptions from SQL batch #995

@fhossfel

Description

@fhossfel

Driver version

7.2.1.jre8

SQL Server version

Microsoft SQL Server 2017 (RTM-CU13) (KB4466404) - 14.0.3048.4 (X64)
Nov 30 2018 12:57:58
Copyright (C) 2017 Microsoft Corporation
Express Edition (64-bit) on Linux (Ubuntu 16.04.5 LTS)

Client Operating System

Windows 10

JAVA/JVM version

java version "1.8.0_152"
Java(TM) SE Runtime Environment (build 1.8.0_152-b16)
Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)

Table schema

master

Problem description

  1. Expected behaviour:
    If I run a batch of SQL statements through PreparedStatement I expect any exception encountered to be thrown. This is what happens if you use a Statment

  2. Actual behaviour:
    No exeption is thrown.

  3. Error message/stack trace:
    Nothing!

  4. Any other details that can be helpful:
    This might be related to Throw the initial batchException  #458

Reproduction code

pom.xml

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>de.thoughtgang.otto</groupId>
    <artifactId>sql-server-test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.surefire.plugin.version>2.22.1</maven.surefire.plugin.version>
        <junit.jupiter.version>5.3.1</junit.jupiter.version>
        <junit.platform.version>1.4.0</junit.platform.version>	
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <version>7.2.1.jre8</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surefire.plugin.version}</version>
            </plugin>
        </plugins>
    </build>
`
</project>

SQLServerExceptionTest:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package de.thoughtgang.microsoft.sqlserver;

import com.microsoft.sqlserver.jdbc.SQLServerException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.junit.jupiter.api.AfterAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;

/**
 *
 * @author FEHOSSFE
 */
@TestInstance(Lifecycle.PER_CLASS)
public class SQLServerExceptionTest {

    private static final String URL = "jdbc:sqlserver://localhost;databaseName=master";
    private static final String USERNAME = "sa";
    private static final String PASSWORD = "MSSQLServer2017";
    private static final String SQL = "SELECT 1; INSERT INTO test (test) VALUES (23);";

    private Connection con;

    @BeforeAll
    public void beforeAll() throws ClassNotFoundException, SQLException {

        Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
        con = DriverManager.getConnection(URL, USERNAME, PASSWORD);

    }

    @AfterAll
    public void afterAll() throws SQLException {

        if (con != null && !con.isClosed()) {

            con.close();

        }

    }

    /**
     * This unit test will execute the SQL string consisting of multiple
     * statments but using a Prepared Statement. It fails to detect the error
     * that occurs because there is not table 'test'.
     */
    @Test
    public void testPreparedStatement() {

        SQLServerException sqlex = assertThrows(SQLServerException.class, () -> {
            boolean hasMoreResultSets;
            try (PreparedStatement stmt = con.prepareStatement(SQL);) {

                hasMoreResultSets = stmt.execute();
                System.out.println(hasMoreResultSets);

                while (hasMoreResultSets) {

                    System.out.println("Resultset found");
                    ResultSet rs = stmt.getResultSet();
                    rs.next();
                    String test = rs.getString(1);
                    rs.close();

                    hasMoreResultSets = stmt.getMoreResults();

                }

                System.out.println(stmt.getMoreResults());

            }

        });

        assertEquals("Invalid object name 'test'.", sqlex.getMessage());

    }

    /**
     * This unit test will execute the SQL string consisting of multiple
     * statments and successfully detect that an error has occured.
     */
    @Test
    public void testStatement() {

        SQLServerException sqlex = assertThrows(SQLServerException.class, () -> {
            boolean hasMoreResultSets;
            try (Statement stmt = con.createStatement();) {

                hasMoreResultSets = stmt.execute(SQL);

                System.out.println(hasMoreResultSets);

                 while (hasMoreResultSets) {

                    System.out.println("Resultset found");
                    ResultSet rs = stmt.getResultSet();
                    rs.next();
                    rs.close();

                    hasMoreResultSets = stmt.getMoreResults();

                } 

                System.out.println(stmt.getMoreResults()); 
            }
        });

        assertEquals("Invalid object name 'test'.", sqlex.getMessage());

    }

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions