content: /*
   * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
   *
   * This program is free software; you can redistribute it and/or modify it under
   * the terms of the GNU General Public License, version 2.0, as published by the
   * Free Software Foundation.
   *
   * This program is also distributed with certain software (including but not
   * limited to OpenSSL) that is licensed under separate terms, as designated in a
   * particular file or component or in included license documentation. The
   * authors of MySQL hereby grant you an additional permission to link the
   * program and your derivative works with the separately licensed software that
   * they have included with MySQL.
   *
   * Without limiting anything contained in the foregoing, this file, which is
   * part of MySQL Connector/J, is also subject to the Universal FOSS Exception,
   * version 1.0, a copy of which can be found at
   * http://oss.oracle.com/licenses/universal-foss-exception.
   *
   * This program is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
   * for more details.
   *
   * You should have received a copy of the GNU General Public License along with
   * this program; if not, write to the Free Software Foundation, Inc.,
   * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
   */
  
  package testsuite.x.internal;
  
  import static org.junit.jupiter.api.Assertions.assertEquals;
  import static org.junit.jupiter.api.Assertions.assertFalse;
  import static org.junit.jupiter.api.Assertions.assertTrue;
  import static org.junit.jupiter.api.Assumptions.assumeTrue;
  
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.function.BiConsumer;
  import java.util.stream.Collectors;
  import java.util.stream.StreamSupport;
  
  import org.junit.jupiter.api.AfterEach;
  import org.junit.jupiter.api.BeforeEach;
  import org.junit.jupiter.api.Test;
  
  import com.mysql.cj.MysqlType;
  import com.mysql.cj.conf.PropertyDefinitions;
  import com.mysql.cj.exceptions.MysqlErrorNumbers;
  import com.mysql.cj.protocol.ColumnDefinition;
  import com.mysql.cj.protocol.x.StatementExecuteOkBuilder;
  import com.mysql.cj.protocol.x.XMessageBuilder;
  import com.mysql.cj.protocol.x.XProtocol;
  import com.mysql.cj.protocol.x.XProtocolError;
  import com.mysql.cj.protocol.x.XProtocolRowInputStream;
  import com.mysql.cj.protocol.x.XServerCapabilities;
  import com.mysql.cj.result.Field;
  import com.mysql.cj.result.Row;
  import com.mysql.cj.result.StringValueFactory;
  import com.mysql.cj.xdevapi.DocFilterParams;
  import com.mysql.cj.xdevapi.FilterParams;
  import com.mysql.cj.xdevapi.InsertParams;
  import com.mysql.cj.xdevapi.SqlResult;
  import com.mysql.cj.xdevapi.SqlResultBuilder;
  import com.mysql.cj.xdevapi.TableFilterParams;
  import com.mysql.cj.xdevapi.UpdateSpec;
  import com.mysql.cj.xdevapi.UpdateType;
  import com.mysql.cj.xdevapi.Warning;
  import com.mysql.cj.xdevapi.WarningImpl;
  
  /**
   * Tests for protocol-level APIs against X Plugin via X Protocol.
   */
  public class XProtocolTest extends InternalXBaseTestCase {
      private XProtocol protocol;
      private XMessageBuilder messageBuilder;
  
      @BeforeEach
      public void setupTestProtocol() {
          if (this.isSetForXTests) {
              this.protocol = createAuthenticatedTestProtocol(createTestProtocol(), this.testProperties);
              this.messageBuilder = (XMessageBuilder) this.protocol.getMessageBuilder();
          }
      }
  
      @AfterEach
      public void destroyTestProtocol() throws IOException {
          if (this.isSetForXTests && this.protocol != null) {
              try {
                  this.protocol.close();
              } catch (Exception ex) {
                  System.err.println("Exception during destroy");
                  ex.printStackTrace();
              }
          }
      }
  
      /**
       * Test the create/drop collection admin commands.
       */
      @Test
      public void testCreateAndDropCollection() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          try {
              this.protocol.send(this.messageBuilder.buildCreateCollection(getTestDatabase(), "testCreateAndDropCollection"), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
          } catch (XProtocolError err) {
              // leftovers, clean them up now
              if (err.getErrorCode() == MysqlErrorNumbers.ER_TABLE_EXISTS_ERROR) {
                  this.protocol.send(this.messageBuilder.buildDropCollection(getTestDatabase(), "testCreateAndDropCollection"), 0);
                  this.protocol.readQueryResult(new StatementExecuteOkBuilder());
                  // try again
                  this.protocol.send(this.messageBuilder.buildCreateCollection(getTestDatabase(), "testCreateAndDropCollection"), 0);
                  this.protocol.readQueryResult(new StatementExecuteOkBuilder());
              } else {
                  throw err;
              }
          }
          // we don't verify the existence. That's the job of the server/xplugin
          this.protocol.send(this.messageBuilder.buildDropCollection(getTestDatabase(), "testCreateAndDropCollection"), 0);
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
      }
  
      @Test
      public void testTrivialSqlQuery() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("select 'x' as y"), 0);
          assertTrue(this.protocol.hasResults());
          ColumnDefinition metadata = this.protocol.readMetadata();
          assertEquals(1, metadata.getFields().length);
          Field f = metadata.getFields()[0];
          // not an exhaustive metadata test
          assertEquals("y", f.getColumnLabel());
          assertEquals(MysqlType.FIELD_TYPE_VARCHAR, f.getMysqlTypeId());
          Iterator<Row> rowInputStream = new XProtocolRowInputStream(metadata, this.protocol, null);
          Row r = rowInputStream.next();
          String value = r.getValue(0, new StringValueFactory(this.protocol.getPropertySet()));
          assertEquals("x", value);
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
      }
  
      @Test
      public void testAnotherBasicSqlQuery() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          this.protocol.send(this.messageBuilder
                  .buildSqlStatement("select 'x' as a_string, 42 as a_long, 7.6 as a_decimal union select 'y' as a_string, 11 as a_long, .1111 as a_decimal"), 0);
          assertTrue(this.protocol.hasResults());
          ColumnDefinition metadata = this.protocol.readMetadata();
          assertEquals(3, metadata.getFields().length);
          assertEquals("a_string", metadata.getFields()[0].getColumnLabel());
          assertEquals("a_long", metadata.getFields()[1].getColumnLabel());
          assertEquals("a_decimal", metadata.getFields()[2].getColumnLabel());
          assertEquals(MysqlType.FIELD_TYPE_VARCHAR, metadata.getFields()[0].getMysqlTypeId());
          assertEquals(MysqlType.FIELD_TYPE_LONGLONG, metadata.getFields()[1].getMysqlTypeId());
          assertEquals(MysqlType.FIELD_TYPE_NEWDECIMAL, metadata.getFields()[2].getMysqlTypeId());
          Iterator<Row> rowInputStream = new XProtocolRowInputStream(metadata, this.protocol, null);
  
          // first row
          Row r = rowInputStream.next();
          String value = r.getValue(0, new StringValueFactory(this.protocol.getPropertySet()));
          assertEquals("x", value);
          value = r.getValue(1, new StringValueFactory(this.protocol.getPropertySet()));
          assertEquals("42", value);
          value = r.getValue(2, new StringValueFactory(this.protocol.getPropertySet()));
          assertEquals("7.6000", value); // TODO: zeroes ok here??? scale is adjusted to 4 due to ".1111" value in RS? not happening in decimal test down below
  
          // second row
          assertTrue(rowInputStream.hasNext());
          r = rowInputStream.next();
          value = r.getValue(0, new StringValueFactory(this.protocol.getPropertySet()));
          assertEquals("y", value);
          value = r.getValue(1, new StringValueFactory(this.protocol.getPropertySet()));
          assertEquals("11", value);
          value = r.getValue(2, new StringValueFactory(this.protocol.getPropertySet()));
          assertEquals("0.1111", value);
  
          assertFalse(rowInputStream.hasNext());
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
      }
  
      /**
       * This tests that all types are decoded correctly. We retrieve them all as strings which happens after the decoding step. This is an exhaustive types of
       * type decoding and metadata from the server.
       */
      @Test
      public void testDecodingAllTypes() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          try {
              // some types depend on this table
              this.protocol.send(this.messageBuilder.buildSqlStatement("drop table if exists xprotocol_types_test"), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
              String testTable = "create table xprotocol_types_test (";
              testTable += " a_float float";
              testTable += ",a_set SET('abc', 'def', 'xyz')";
              testTable += ",an_enum ENUM('enum value a', 'enum value b')";
              testTable += ",an_unsigned_int bigint unsigned";
              this.protocol.send(this.messageBuilder.buildSqlStatement(testTable + ")"), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
              this.protocol.send(
                      this.messageBuilder.buildSqlStatement("insert into xprotocol_types_test values ('2.42', 'xyz,def', 'enum value a', 9223372036854775808)"),
                      0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
              Map<String, BiConsumer<ColumnDefinition, Row>> tests = new HashMap<>();
              tests.put("'some string' as a_string", (metadata, row) -> {
                  assertEquals("a_string", metadata.getFields()[0].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_VARCHAR, metadata.getFields()[0].getMysqlTypeId());
                  assertEquals("some string", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("date('2015-03-22') as a_date", (metadata, row) -> {
                  assertEquals("a_date", metadata.getFields()[0].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_DATETIME, metadata.getFields()[0].getMysqlTypeId());
                  assertEquals("2015-03-22", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("curtime() as curtime, cast(curtime() as char(8)) as curtime_string", (metadata, row) -> {
                  assertEquals("curtime", metadata.getFields()[0].getColumnLabel());
                  assertEquals("curtime_string", metadata.getFields()[1].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_TIME, metadata.getFields()[0].getMysqlTypeId());
                  String curtimeString = row.getValue(1, new StringValueFactory(this.protocol.getPropertySet()));
                  assertEquals(curtimeString, row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("timestamp('2015-05-01 12:01:32') as a_datetime", (metadata, row) -> {
                  assertEquals("a_datetime", metadata.getFields()[0].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_DATETIME, metadata.getFields()[0].getMysqlTypeId());
                  assertEquals("2015-05-01 12:01:32", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("cos(1) as a_double", (metadata, row) -> {
                  assertEquals("a_double", metadata.getFields()[0].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_DOUBLE, metadata.getFields()[0].getMysqlTypeId());
                  // value is 0.5403023058681398. Test most of it
                  assertTrue(row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())).startsWith("0.540302305868139"));
              });
              tests.put("2142 as an_int", (metadata, row) -> {
                  assertEquals("an_int", metadata.getFields()[0].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_LONGLONG, metadata.getFields()[0].getMysqlTypeId());
                  assertEquals("2142", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("21.424 as decimal1, -1.0 as decimal2, -0.1 as decimal3, 1000.0 as decimal4", (metadata, row) -> {
                  assertEquals("decimal1", metadata.getFields()[0].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_NEWDECIMAL, metadata.getFields()[0].getMysqlTypeId());
                  assertEquals("21.424", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
  
                  assertEquals("decimal2", metadata.getFields()[1].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_NEWDECIMAL, metadata.getFields()[1].getMysqlTypeId());
                  assertEquals("-1.0", row.getValue(1, new StringValueFactory(this.protocol.getPropertySet())));
  
                  assertEquals("decimal3", metadata.getFields()[2].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_NEWDECIMAL, metadata.getFields()[2].getMysqlTypeId());
                  assertEquals("-0.1", row.getValue(2, new StringValueFactory(this.protocol.getPropertySet())));
  
                  assertEquals("decimal4", metadata.getFields()[3].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_NEWDECIMAL, metadata.getFields()[3].getMysqlTypeId());
                  assertEquals("1000.0", row.getValue(3, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("9223372036854775807 as a_large_integer", (metadata, row) -> {
                  // max signed 64bit integer
                  assertEquals("a_large_integer", metadata.getFields()[0].getColumnLabel());
                  assertEquals(MysqlType.FIELD_TYPE_LONGLONG, metadata.getFields()[0].getMysqlTypeId());
                  assertEquals("9223372036854775807", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("a_float, a_set, an_enum from xprotocol_types_test", (metadata, row) -> {
                  assertEquals("a_float", metadata.getFields()[0].getColumnLabel());
                  assertEquals("xprotocol_types_test", metadata.getFields()[0].getTableName());
                  assertEquals("2.4200000762939453", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
  
                  assertEquals("a_set", metadata.getFields()[1].getColumnLabel());
                  assertEquals("xprotocol_types_test", metadata.getFields()[1].getTableName());
                  assertEquals("def,xyz", row.getValue(1, new StringValueFactory(this.protocol.getPropertySet())));
  
                  assertEquals("an_enum", metadata.getFields()[2].getColumnLabel());
                  assertEquals("xprotocol_types_test", metadata.getFields()[2].getTableName());
                  assertEquals("enum value a", row.getValue(2, new StringValueFactory(this.protocol.getPropertySet())));
              });
              tests.put("an_unsigned_int from xprotocol_types_test", (metadata, row) -> {
                  assertEquals("an_unsigned_int", metadata.getFields()[0].getColumnLabel());
                  assertEquals("9223372036854775808", row.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              });
  
              // runner for above tests
              for (Map.Entry<String, BiConsumer<ColumnDefinition, Row>> t : tests.entrySet()) {
                  this.protocol.send(this.messageBuilder.buildSqlStatement("select " + t.getKey()), 0);
                  assertTrue(this.protocol.hasResults());
                  ColumnDefinition metadata = this.protocol.readMetadata();
                  Iterator<Row> rowInputStream = new XProtocolRowInputStream(metadata, this.protocol, null);
                  t.getValue().accept(metadata, rowInputStream.next());
                  this.protocol.readQueryResult(new StatementExecuteOkBuilder());
              }
          } finally {
              this.protocol.send(this.messageBuilder.buildSqlStatement("drop table if exists xprotocol_types_test"), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
          }
      }
  
      /**
       * Test DML that is executed with <i>StmtExecute</i> and does not return a result set.
       */
      @Test
      public void testSqlDml() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("drop table if exists mysqlx_sqlDmlTest"), 0);
          assertFalse(this.protocol.hasResults());
          SqlResult res = this.protocol
                  .readQueryResult(new SqlResultBuilder(this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getPropertySet()));
          assertEquals(0, res.getAffectedItemsCount());
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("create table mysqlx_sqlDmlTest (w int primary key auto_increment, x int) auto_increment = 7"),
                  0);
          assertFalse(this.protocol.hasResults());
          res = this.protocol.readQueryResult(new SqlResultBuilder(this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getPropertySet()));
          assertEquals(0, res.getAffectedItemsCount());
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("insert into mysqlx_sqlDmlTest (x) values (44),(29)"), 0);
          assertFalse(this.protocol.hasResults());
          res = this.protocol.readQueryResult(new SqlResultBuilder(this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getPropertySet()));
          assertEquals(2, res.getAffectedItemsCount());
          assertEquals(new Long(7), res.getAutoIncrementValue());
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("drop table mysqlx_sqlDmlTest"), 0);
          assertFalse(this.protocol.hasResults());
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
      }
  
      @Test
      public void testBasicCrudInsertFind() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          try {
              String collName = createTempTestCollection(this.protocol);
  
              String json = "{'_id': '85983efc2a9a11e5b345feff819cdc9f', 'testVal': 1, 'insertedBy': 'Jess'}".replaceAll("'", "\"");
              this.protocol.send(this.messageBuilder.buildDocInsert(getTestDatabase(), collName, Arrays.asList(new String[] { json }), false), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
              FilterParams filterParams = new DocFilterParams(getTestDatabase(), collName);
              filterParams.setCriteria("$.testVal = 2-1");
              this.protocol.send(this.messageBuilder.buildFind(filterParams), 0);
  
              ColumnDefinition metadata = this.protocol.readMetadata();
              Iterator<Row> ris = new XProtocolRowInputStream(metadata, this.protocol, null);
              Row r = ris.next();
              assertEquals(json, r.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
          } finally {
              dropTempTestCollection(this.protocol);
          }
      }
  
      @Test
      public void testMultiInsert() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          try {
              String collName = createTempTestCollection(this.protocol);
  
              List<String> stringDocs = new ArrayList<>();
              stringDocs.add("{'a': 'A', 'a1': 'A1', '_id': 'a'}");
              stringDocs.add("{'b': 'B', 'b2': 'B2', '_id': 'b'}");
              stringDocs.add("{'c': 'C', 'c3': 'C3', '_id': 'c'}");
              stringDocs = stringDocs.stream().map(s -> s.replaceAll("'", "\"")).collect(Collectors.toList());
              this.protocol.send(this.messageBuilder.buildDocInsert(getTestDatabase(), collName, stringDocs, false), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
              FilterParams filterParams = new DocFilterParams(getTestDatabase(), collName);
              filterParams.setOrder("_id");
              this.protocol.send(this.messageBuilder.buildFind(filterParams), 0);
  
              ColumnDefinition metadata = this.protocol.readMetadata();
              Iterator<Row> ris = new XProtocolRowInputStream(metadata, this.protocol, null);
              Row r = ris.next();
              assertEquals(stringDocs.get(0), r.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              r = ris.next();
              assertEquals(stringDocs.get(1), r.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              r = ris.next();
              assertEquals(stringDocs.get(2), r.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
          } finally {
              dropTempTestCollection(this.protocol);
          }
      }
  
      @Test
      public void testDocUpdate() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          try {
              String collName = createTempTestCollection(this.protocol);
  
              String json = "{'_id': '85983efc2a9a11e5b345feff819cdc9f', 'testVal': '1', 'insertedBy': 'Jess'}".replaceAll("'", "\"");
              this.protocol.send(this.messageBuilder.buildDocInsert(getTestDatabase(), collName, Arrays.asList(new String[] { json }), false), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
              List<UpdateSpec> updates = new ArrayList<>();
              updates.add(new UpdateSpec(UpdateType.ITEM_SET, "$.a").setValue("lemon"));
              updates.add(new UpdateSpec(UpdateType.ITEM_REMOVE, "$.insertedBy"));
              this.protocol.send(this.messageBuilder.buildDocUpdate(new DocFilterParams(getTestDatabase(), collName), updates), 0);
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
              // verify
              this.protocol.send(this.messageBuilder.buildFind(new DocFilterParams(getTestDatabase(), collName)), 0);
              ColumnDefinition metadata = this.protocol.readMetadata();
              Iterator<Row> ris = new XProtocolRowInputStream(metadata, this.protocol, null);
              Row r = ris.next();
              assertEquals("{\"a\": \"lemon\", \"_id\": \"85983efc2a9a11e5b345feff819cdc9f\", \"testVal\": \"1\"}",
                      r.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
              this.protocol.readQueryResult(new StatementExecuteOkBuilder());
          } finally {
              dropTempTestCollection(this.protocol);
          }
      }
  
      @Test
      public void tableInsert() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("drop table if exists tableInsert"), 0);
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
          this.protocol.send(this.messageBuilder.buildSqlStatement("create table tableInsert (x int, y varchar(20), z decimal(10, 2))"), 0);
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
          InsertParams insertParams = new InsertParams();
          insertParams.setProjection(new String[] { "z", "x", "y" });
          insertParams.addRow(Arrays.asList("10.2", 40, "some string value"));
          insertParams.addRow(Arrays.asList("10.3", 50, "another string value"));
          this.protocol.send(this.messageBuilder.buildRowInsert(getTestDatabase(), "tableInsert", insertParams), 0);
          SqlResult res = this.protocol
                  .readQueryResult(new SqlResultBuilder(this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getPropertySet()));
          assertEquals(2, res.getAffectedItemsCount());
  
          FilterParams filterParams = new TableFilterParams(getTestDatabase(), "tableInsert");
          filterParams.setOrder("x DESC");
          filterParams.setFields("z, y, x");
          this.protocol.send(this.messageBuilder.buildFind(filterParams), 0);
  
          ColumnDefinition metadata = this.protocol.readMetadata();
          Iterator<Row> ris = new XProtocolRowInputStream(metadata, this.protocol, null);
          Row r = ris.next();
          assertEquals("10.30", r.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
          assertEquals("another string value", r.getValue(1, new StringValueFactory(this.protocol.getPropertySet())));
          assertEquals("50", r.getValue(2, new StringValueFactory(this.protocol.getPropertySet())));
          r = ris.next();
          assertEquals("10.20", r.getValue(0, new StringValueFactory(this.protocol.getPropertySet())));
          assertEquals("some string value", r.getValue(1, new StringValueFactory(this.protocol.getPropertySet())));
          assertEquals("40", r.getValue(2, new StringValueFactory(this.protocol.getPropertySet())));
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("drop table tableInsert"), 0);
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
      }
  
      @Test
      public void testWarnings() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("explain select 1"), 0);
          this.protocol.readMetadata();
          this.protocol.drainRows();
          SqlResult res = this.protocol
                  .readQueryResult(new SqlResultBuilder(this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getPropertySet()));
          Iterable<Warning> iterable = () -> res.getWarnings();
          List<Warning> warnings = StreamSupport.stream(iterable.spliterator(), false).map(w -> new WarningImpl(w)).collect(Collectors.toList());
  
          assertEquals(1, warnings.size());
          Warning w = warnings.get(0);
          assertEquals(1, w.getLevel());
          assertEquals(1003, w.getCode());
          // this message format might change over time and have to be loosened up
          assertEquals("/* select#1 */ select 1 AS `1`", w.getMessage());
      }
  
      @Test
      public void testEnableDisableNotices() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          this.protocol.send(this.messageBuilder.buildDisableNotices("warnings"), 0); // TODO currently only "warnings" are allowed to be disabled
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("select CAST('abc' as CHAR(1))"), 0);
          new XProtocolRowInputStream(this.protocol.readMetadata(), this.protocol, null).next();
          SqlResult res = this.protocol
                  .readQueryResult(new SqlResultBuilder(this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getPropertySet()));
          assertEquals(0, res.getWarningsCount());
  
          // "produced_message" are already enabled, they're used here to check that multiple parameters are sent correctly
          this.protocol.send(this.messageBuilder.buildEnableNotices("produced_message", "warnings"), 0);
  
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
  
          this.protocol.send(this.messageBuilder.buildSqlStatement("select CAST('abc' as CHAR(1))"), 0);
          new XProtocolRowInputStream(this.protocol.readMetadata(), this.protocol, null).next();
          res = this.protocol.readQueryResult(new SqlResultBuilder(this.protocol.getServerSession().getDefaultTimeZone(), this.protocol.getPropertySet()));
          assertEquals(1, res.getWarningsCount());
      }
  
      /**
       * This is a development method that will print a detailed result set for any command sent.
       */
      @Test
      public void testResultSet() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          // begin "send" stage, change this as necessary
          this.protocol.send(this.messageBuilder.buildListNotices(), 0);
  
          // this will read the metadata and result and print all data
          ColumnDefinition metadata = this.protocol.readMetadata();
          Arrays.stream(metadata.getFields()).forEach(f -> {
              System.err.println("***************** field ****************");
              System.err.println("Field: " + f.getColumnLabel());
              System.err.println("Type: " + f.getMysqlTypeId());
              System.err.println("Encoding: " + f.getEncoding());
          });
  
          Iterator<Row> ris = new XProtocolRowInputStream(metadata, this.protocol, null);
          ris.forEachRemaining(r -> {
              System.err.println("***************** row ****************");
              for (int i = 0; i < metadata.getFields().length; ++i) {
                  System.err.println(metadata.getFields()[i].getColumnLabel() + ": " + r.getValue(i, new StringValueFactory(this.protocol.getPropertySet())));
              }
          });
  
          this.protocol.readQueryResult(new StatementExecuteOkBuilder());
      }
  
      @Test
      public void testCapabilities() {
          assumeTrue(this.isSetForXTests, PropertyDefinitions.SYSP_testsuite_url_mysqlx + " must be set to run this test.");
  
          XServerCapabilities capabilities = (XServerCapabilities) this.protocol.getServerSession().getCapabilities();
  
          assertEquals("mysql", capabilities.getNodeType());
          assertTrue(capabilities.getTls());
          assertFalse(capabilities.getClientPwdExpireOk());
          assertTrue(capabilities.getAuthenticationMechanisms().contains("MYSQL41"));
          assertEquals("text", capabilities.getDocFormats());
      }
  }
  