[CONJ-22] Java Client library does not support useServerPrepStmts Created: 2013-02-06  Updated: 2016-05-12  Resolved: 2015-09-15

Status: Closed
Project: MariaDB Connector/J
Component/s: Other
Affects Version/s: 1.1.0, 1.2.0
Fix Version/s: 1.3.0

Type: Epic Priority: Major
Reporter: Elena Stepanova Assignee: Diego Dupin
Resolution: Fixed Votes: 1
Labels: None

Issue Links:
Blocks
blocks CONJ-26 [Feature Request] Implement configura... Closed
is blocked by CONJ-170 There is an memory leak in mariadb cl... Closed
is blocked by CONJ-257 Permit use of server preparedStatemen... Closed
Relates
Epic Name: Use server prepareStatement
Sprint: Sprint connector/j 1.3.0

 Description   

To enable prepared statements on the server side, MySQL Connector/J provides useServerPrepStmts parameter.
It doesn't work for MariaDB client. If there is a way to get prepared statements work through MariaDB client, I haven't yet found it.

The provided test case checks the global value of Prepared_stmt_count status variable, then attempts to prepare a statement on a connection with useServerPrepStmts, then checks the variable value again.

Output with MariaDB client library 1.1.0:

Prepared_stmt_count before prepare: 0
Prepared_stmt_count after prepare: 0

With MySQL Connector/J 5.1.23:

Prepared_stmt_count before prepare: 0
Prepared_stmt_count after prepare: 1

Test case:

import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.PreparedStatement;
 
 
public class BugPreparedStatement
{
  public static void main (String argv[])  
  {
    try {
 
      Statement status = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","").createStatement();
 
      ResultSet rs = status.executeQuery("show global status like 'Prepared_stmt_count'");
      if (rs.first()) {
        System.out.println("Prepared_stmt_count before prepare: " + rs.getInt(2));  
      }
 
      PreparedStatement pst = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useServerPrepStmts=true","root","").prepareStatement("select 1");
 
      rs = status.executeQuery("show global status like 'Prepared_stmt_count'");
      if (rs.first()) {
        System.out.println("Prepared_stmt_count after prepare: " + rs.getInt(2));  
      }
    }
    catch (Exception e)
    {
      System.out.println("Exception: " + e + "\n");
      e.printStackTrace();
    }
  } 
}
 



 Comments   
Comment by Vladislav Vaintroub [ 2013-02-06 ]

This is a feature request, a rather big feature (i.e connector-big , not server-big

The driver documentation https://kb.askmonty.org/en/about-the-mariadb-java-client/ lists all supported parameters and also states
"The driver only uses text protocol to communicate with the database. Prepared statements (parameter substitution) is handled by the driver, on the client side."

This is basically what this bug is all about. The only unique feature that server side prepared statement allows is possibility to get result set metadata of the prepared statement without execution (CONJ-21)

Comment by Elena Stepanova [ 2013-02-07 ]

Strictly speaking, apart from the unique feature, there is probably also performance at stake (I didn't run benchmarks, but I expect server-side prepared statements to be faster); but otherwise I agree, it's a feature request, and not even a critical one, considering the number of problems with prepared statements on the server side. Sorry for not finding it on the KB page.

Unfortunately, the lack of the feature described in CONJ-21 means that client applications relying on it and trying to switch from connector/j to mariadb library will start throwing rather ugly null pointer exceptions.

Comment by Andy Shulman [ 2013-11-03 ]

I'd just like to +1 this issue. I just switched from the Oracle JDBC driver because it didn't support PreparedStatement rewriting. I'm sad to lose server-side PreparedStatements as they are quite a bit faster for my application (though rewriting is a much bigger increase). Getting both features in one driver would be fantastic.

Comment by Paolo Bazzi [ 2015-07-06 ]

Using the new MariaDB JDBC Driver 1.1.9 I'm still not able to show that server-prepared statements are working.

Using the attached test case from above still prints out the following:
Prepared_stmt_count before prepare: 0
Prepared_stmt_count after prepare: 0

If I switch the testcase and use the mysql-connector-java-5.1.32-bin.jar JDBC driver, the output is as expected:
Prepared_stmt_count before prepare: 0
Prepared_stmt_count after prepare: 1

Could you check again, if this issues was really solved with 1.1.9?

Comment by diego dupin (Inactive) [ 2015-07-06 ]

The PreparedStatement rewriting has been fixed, but that doesn't include this issue.
it is indeed not fixed.

Comment by Marcel Schneider [ 2016-01-15 ]

I verified this with version 1.3.3, and it seems that the useServerPrepStmts parameter still does not work.
I suggest to reopen the ticket.

Comment by Marcel Schneider [ 2016-01-27 ]

Shall i make a new ticket?

Comment by Diego Dupin [ 2016-01-29 ]

Hi,
I check again and this work.
Can you elaborate on the problem you have ?

some tests case i've run:

    @Test
    public void serverExecutionTest() throws SQLException {
        createTable("ServerPrepareStatementTestt", "id int not null primary key auto_increment, test boolean");
        try (Connection connection = setConnection()){
            Statement statement = connection.createStatement();
            ResultSet rs = statement.executeQuery("show global status like 'Prepared_stmt_count'");
            assertTrue(rs.next());
            final int nbStatementCount = rs.getInt(2);
 
            PreparedStatement ps = connection.prepareStatement(
                    "INSERT INTO ServerPrepareStatementTestt (test) VALUES (?)");
            ps.setBoolean(1, true);
            ps.addBatch();
            ps.execute();
 
            rs = statement.executeQuery("show global status like 'Prepared_stmt_count'");
            assertTrue(rs.next());
            assertTrue(rs.getInt(2) == nbStatementCount + 1);
        }
    }

    @Test
    public void testCache() throws SQLException {
        createTable("ServerPrepareStatementPrepareCache", "id int not null primary key auto_increment, test varchar(20)");
        try (Connection connection = setConnection()) {
            final String QUERY = "INSERT INTO ServerPrepareStatementPrepareCache(test) VALUES (?)";
            final long startTime = System.nanoTime();
            PreparedStatement pstmt = connection.prepareStatement(QUERY);
            pstmt.setString(1, "test1");
            pstmt.execute();
            final long executionTime = System.nanoTime() - startTime;
 
            final long startTimeSecond = System.nanoTime();
            PreparedStatement pstmt2 = connection.prepareStatement(QUERY);
            pstmt2.setString(1, "test2");
            pstmt2.execute();
            final long executionTimeSecond = System.nanoTime() - startTimeSecond;
 
            System.out.println("total time : " + (executionTimeSecond) + " first : " + executionTime);
            Assert.assertTrue(executionTimeSecond  * 10 < executionTime);
 
            ResultSet resultSet = connection.createStatement().executeQuery("SELECT * FROM ServerPrepareStatementPrepareCache");
            if (resultSet.next()) {
                Assert.assertEquals("test1", resultSet.getString(2));
                if (resultSet.next()) {
                    Assert.assertEquals("test2", resultSet.getString(2));
                } else {
                    Assert.fail("Must have a result");
                }
            } else {
                Assert.fail("Must have a result");
            }
        }
    }

console result is :
second query is total time : 2788435 first : 59721444

Comment by Marcel Schneider [ 2016-01-29 ]

I can reproduce your test cases: they work as intended. So I went back to my case which doesn't work. After some analyses I found this:
the caching does work if you use a statement with a bind, like this:
"select count(1) from ServerPrepareStatementTestt where id=?".
However, the caching does not work if you don't use a bind, like this:
"select count(1) from ServerPrepareStatementTestt where 1=1".
When I switch to the mysql driver, the caching works in both cases.

Comment by Marcel Schneider [ 2016-05-12 ]

Were you able to reproduce my results (using literals instead of binds)? Showing that the issue is still open?

Generated at Thu Feb 08 03:12:35 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.