gt:         String[] lines = captured.split("\n");
context: /* This file is part of VoltDB.
   * Copyright (C) 2008-2018 VoltDB Inc.
   *
   * Permission is hereby granted, free of charge, to any person obtaining
   * a copy of this software and associated documentation files (the
   * "Software"), to deal in the Software without restriction, including
   * without limitation the rights to use, copy, modify, merge, publish,
   * distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to
   * the following conditions:
   *
   * The above copyright notice and this permission notice shall be
   * included in all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
   * OTHER DEALINGS IN THE SOFTWARE.
   */
  
  package org.voltdb.compiler;
  
  import java.io.ByteArrayOutputStream;
  import java.io.PrintStream;
  
  import org.voltdb.VoltDB.Configuration;
  import org.voltdb.compiler.procedures.FloatParamToGetNiceComplaint;
  import org.voltdb.compiler.procedures.InsertAggregatesOfFloat;
  import org.voltdb.compiler.procedures.InsertAggregatesOfFloatInHaving;
  import org.voltdb.compiler.procedures.InsertAggregatesOfFloatWithSetops;
  import org.voltdb_testprocs.regressionsuites.failureprocs.DeterministicRONonSeqProc;
  import org.voltdb_testprocs.regressionsuites.failureprocs.DeterministicROSeqProc;
  import org.voltdb_testprocs.regressionsuites.failureprocs.DeterministicRWProc1;
  import org.voltdb_testprocs.regressionsuites.failureprocs.DeterministicRWProc2;
  import org.voltdb_testprocs.regressionsuites.failureprocs.NondeterministicROProc;
  import org.voltdb_testprocs.regressionsuites.failureprocs.NondeterministicRWProc;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPNoncandidate1;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPNoncandidate2;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPNoncandidate3;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPNoncandidate4;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPNoncandidate5;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPNoncandidate6;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPcandidate1;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPcandidate2;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPcandidate3;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPcandidate4;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPcandidate5;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPcandidate6;
  import org.voltdb_testprocs.regressionsuites.failureprocs.ProcSPcandidate7;
  
  import junit.framework.TestCase;
  
  public class TestVoltCompilerAnnotationsAndWarnings extends TestCase {
  
      /**
       * Test whether a DDL stored procedure does not compile properly. The test
       * testSimple cannot test compilation failure. It's sometimes useful to have
       * a single test to test compilation success.
       *
       * @param simpleSchema
       *            A string containing the test schema.
       * @param procObject
       *            This tells what procedure to test. If this is an object of
       *            type Class<?> we we add its procedures to the builder. If it's
       *            an array of two strings we add a statement procedure whose
       *            name is the first string and whose DDL definition is the
       *            second string. Note that the "create procedure as" part is
       *            added for you, so all you need is the SQL text for the
       *            procedure.
       * @param errorMessages
       *            The error messages we expect to see, as Java regular
       *            expressions. This may be true of no errors are expected.
       * @param expectSuccess
       *            If this is true, the compilation should succeed.
       * @throws Exception
       */
      public void testCompilationFailure(String     testName,
                                         String     simpleSchema,
                                         Object     procObject,
                                         String[]   errorMessages,
                                         boolean    expectSuccess) throws Exception {
          VoltProjectBuilder builder = new VoltProjectBuilder();
          ByteArrayOutputStream capturer = new ByteArrayOutputStream();
          PrintStream capturing = new PrintStream(capturer);
          builder.setCompilerDebugPrintStream(capturing);
          builder.addLiteralSchema(simpleSchema);
          if (procObject instanceof String[]) {
              String[] stmtProcDescrip = (String[]) procObject;
              assertTrue(stmtProcDescrip.length == 2);
              builder.addStmtProcedure(stmtProcDescrip[0], stmtProcDescrip[1]);
          } else if (procObject instanceof Class<?>) {
              Class<?> procKlazz = (Class<?>) procObject;
              builder.addProcedure(procKlazz);
          } else {
              assertTrue("Bad type of object for parameter \"procObject\"", false);
          }
  
          boolean success = builder.compile(Configuration.getPathToCatalogForTest("annotations.jar"));
          assertEquals(String.format("Expected compilation %s",
                                     (expectSuccess ? "success" : "failure")),
                       expectSuccess, success);
          if (errorMessages != null) {
              String captured = capturer.toString("UTF-8");
              String[] lines = captured.split("\n");
              System.out.printf("\n" + ":----------------------------------------------------------------------:\n" + ":  %s: Start of captured output\n" + ":----------------------------------------------------------------------:\n",
                                testName);
              System.out.println(captured);
              System.out.printf("\n" + ":----------------------------------------------------------------------:\n" + ":  %s: End of captured output\n" + ":----------------------------------------------------------------------:\n",
                                testName);
              // Output should include a line suggesting replacement of float with
              // double.
              for (String oneMessagePattern : errorMessages) {
                  assertTrue(foundLineMatching(lines, oneMessagePattern));
              }
          }
      }
  
      /**
       * Test that a stored procedure with a parameter type of float will not
       * compile. The Java type float is not legal for stored procedures.
       *
       * @throws Exception
       */
      public void testFloatParamComplaint() throws Exception {
          String simpleSchema =
              "create table floatie (" +
              "ival bigint default 0 not null, " +
              "fval float not null," +
              "PRIMARY KEY(ival)" +
              ");" +
              "partition table floatie on column ival;";
          testCompilationFailure("testFloatParamComplaint",
                                 simpleSchema,
                                 FloatParamToGetNiceComplaint.class,
                                 new String[] { ".*FloatParamToGetNiceComplaint.* float.* double.*" },
                                 false);
      }
  
      /**
       * Test that a stored procedure with an aggregate whose parameter is FLOAT
       * causes a compilation error.
       *
       * @throws Exception
       */
      public void testInsertAggregatesOfFloat() throws Exception {
          String simpleSchema =
                  "create table floatingaggs_input ( alpha float );" +
                  "create table floatingaggs_output ( beta float );" +
                  "";
          testCompilationFailure("testInsertAggregatesOfFloat",
                                 simpleSchema,
                                 InsertAggregatesOfFloat.class,
                                 new String[] { ".*InsertAggregatesOfFloat.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a stored procedure with an aggregate of floating point type in
       * a having clause causes a compilation error.
       *
       * @throws Exception
       */
      public void testInsertAggregatesOfFloatInHaving() throws Exception {
          String simpleSchema =
                  "create table floatingaggs_input ( alpha float );" +
                  "create table floatingaggs_output ( beta float );" +
                  "create table intaggs ( gamma integer );" +
                  "";
          testCompilationFailure("testInsertAggregatesOfFloatInHaving",
                                 simpleSchema,
                                 InsertAggregatesOfFloatInHaving.class,
                                 new String[] { ".*InsertAggregatesOfFloatInHaving.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a statement procedure with an aggregate of floating point type
       * causes a compilation error.
       *
       * @throws Exception
       */
      public void testAggregatesOfFloatDDL() throws Exception {
          String simpleSchema = "create table floatingaggs_input ( alpha float );" + "create table floatingaggs_output ( beta float );" + "";
          testCompilationFailure("testAggregatesOfFloatDDL",
                                 simpleSchema,
                                 new String[] { "InsertAggregatesOfFloatDDL", "insert into floatingaggs_output select sum(alpha) from floatingaggs_input;",
                                 },
                                 new String[] { ".*InsertAggregatesOfFloatDDL.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a statement procedure with an aggregate of floating point type
       * in a subquery in a from clause causes an error.
       *
       * @throws Exception
       */
      public void testAggregatesOfFloatInSubquery() throws Exception {
          String simpleSchema = "create table floatingaggs_input ( alpha float );" + "create table floatingaggs_output ( beta float );" + "";
          testCompilationFailure("testAggregatesOfFloatInSubquery",
                                 simpleSchema,
                                 new String[] { "AggregatesOfFloatInSubquery", "insert into floatingaggs_output select sq.ss from ( select sum(alpha) as ss from floatingaggs_input where alpha > 0.0 order by ss ) as sq;" },
                                 new String[] { ".*AggregatesOfFloatInSubquery.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a statement procedure with an expression which has a
       * subexpression which is an aggregate of floating point type in a subquery
       * in a from clause causes an error.
       *
       * @throws Exception
       */
      public void testAggregatesOfFloatInComplexSubquery() throws Exception {
          String simpleSchema = "create table floatingaggs_input ( alpha float );" + "create table floatingaggs_output ( beta float );" + "";
          testCompilationFailure("testAggregatesOfFloatInComplexSubquery",
                                 simpleSchema,
                                 new String[] { "AggregatesOfFloatInComplexSubquery", "insert into floatingaggs_output select sq.ss + 100 from ( select sum(alpha) as ss from floatingaggs_input where alpha > 0.0 order by ss ) as sq;" },
                                 new String[] { ".*AggregatesOfFloatInComplexSubquery.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a statement procedure with an expression which has a
       * subexpression which is an aggregate whose type is float and whose
       * expression is more than a column reference and which is also part of a
       * larger expression causes a compilation error.
       *
       * @throws Exception
       */
      public void testAggregatesOfFloatInComplexSubquery2() throws Exception {
          String simpleSchema = "create table floatingaggs_input ( alpha float );" + "create table floatingaggs_output ( beta float );" + "";
          testCompilationFailure("testAggregatesOfFloatInComplexSubquery2",
                                 simpleSchema,
                                 new String[] { "AggregatesOfFloatInComplexSubquery2", "insert into floatingaggs_output select sq.ss + 100 from ( select sum(alpha + 42) as ss from floatingaggs_input where alpha > 0.0 order by ss ) as sq;" },
                                 new String[] { ".*AggregatesOfFloatInComplexSubquery2.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a statement procedure with an expression which has a
       * subexpression which is an aggregate whose type is float and is in a
       * subquery causes a compilation error.
       *
       * @throws Exception
       */
      public void testAggregatesOfFloatInLeftOfJoin() throws Exception {
          String simpleSchema = "create table alpha ( af float );" + "create table beta ( bf float );" + "";
          testCompilationFailure("testAggregatesOfFloatInLeftOfJoin",
                                 simpleSchema,
                                 new String[] { "AggregatesOfFloatInLeftOfJoin",
                                                "insert into alpha select lf.ss+rf.ss from (select sum(af) as ss from alpha ) as lf inner join ( select bf as ss from beta ) as rf on true;" },
                                 new String[] { ".*AggregatesOfFloatInLeftOfJoin.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a statement procedure with an expression which has a
       * subexpression which is an aggregate whose type is float and is in a
       * subquery causes a compilation error.
       *
       * @throws Exception
       */
      public void testAggregatesOfFloatInRightOfJoins() throws Exception {
          String simpleSchema = "create table alpha ( af float );" + "create table beta ( bf float );" + "";
          testCompilationFailure("testAggregatesOfFloatInRightOfJoin",
                                 simpleSchema,
                                 new String[] { "AggregatesOfFloatInRightOfJoin",
                                                "insert into alpha select lf.ss+rf.ss from (select af as ss from alpha ) as lf inner join ( select sum(bf) as ss from beta ) as rf on true;" },
                                 new String[] { ".*AggregatesOfFloatInRightOfJoin.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 false);
      }
  
      /**
       * Test that a statement procedure with an aggregate expression of floating
       * point type in a subquery which is found in a union expression causes a
       * compilation error. This can't be a single statement procedure. It has to
       * be a Java Stored Procedure. This will only be a warning, so the
       * compilation will pass.
       */
      public void testAggregatesOfFloatInSetops() throws Exception {
          String simpleSchema = "create table floatingaggs_input ( alpha float );" + "create table floatingaggs_output ( beta float );" + "";
          testCompilationFailure("testAggregatesOfFloatInSetops",
                                 simpleSchema,
                                 InsertAggregatesOfFloatWithSetops.class,
                                 new String[] { ".*InsertAggregatesOfFloatWithSetops.*Aggregate functions of floating point columns may not be deterministic.  We suggest converting to DECIMAL.*" },
                                 true);
      }
  
      /**
       * Test that Min does not trigger non-determinism errors.
       *
       * @throws Exception
       */
      public void testMinOfFloatIsOk() throws Exception {
          String simpleSchema = "create table alpha ( af float );" + "create table beta ( bf float );" + "";
          testCompilationFailure("testMinOfFloat",
                                 simpleSchema,
                                 new String[] { "MinOfFloat", "insert into alpha select lf.ss+rf.ss from (select af as ss from alpha ) as lf inner join ( select min(bf) as ss from beta ) as rf on true;" },
                                 null,
                                 true);
      }
  
      /**
       * Test that Max does not trigger non-determinism errors.
       *
       * @throws Exception
       */
      public void testMaxOfFloatIsOk() throws Exception {
          String simpleSchema = "create table alpha ( af float );" + "create table beta ( bf float );" + "";
          testCompilationFailure("testMaxOfFloat",
                                 simpleSchema,
                                 new String[] { "MaxOfFloat", "insert into alpha select lf.ss+rf.ss from (select af as ss from alpha ) as lf inner join ( select max(bf) as ss from beta ) as rf on true;" },
                                 null,
                                 true);
      }
  
      /**
       * Test that we haven't broken the obvious good case.
       *
       * @throws Exception
       */
      public void testGoodInsert() throws Exception {
          String simpleSchema = "create table floatingaggs_input ( alpha float );" + "create table floatingaggs_output ( beta float );" + "";
          testCompilationFailure("testAggregatesOfFloatInComplexSubquery2",
                                 simpleSchema,
                                 new String[] { "AggregatesOfFloatInComplexSubquery2", "insert into floatingaggs_output select alpha from floatingaggs_input;" },
                                 null,
                                 true);
      }
  
      public void testSimple() throws Exception {
          String simpleSchema =
              "create table blah (" +
              "ival bigint default 0 not null, " +
              "sval varchar(255) not null" +
              ");" +
              "create table indexed_replicated_blah (" +
              "ival bigint default 0 not null, " +
              "sval varchar(255) not null, " +
              "PRIMARY KEY(ival)" +
              ");" +
              "create table indexed_partitioned_blah (" +
              "ival bigint default 0 not null, " +
              "sval varchar(255) not null, " +
              "PRIMARY KEY(ival)" +
              ");" +
              "create table floatingaggs_input (" +
              "alpha float" +
              ");" +
              "create table floatingaggs_output (" +
              "beta float" +
              ");" +
              "";
  
          VoltProjectBuilder builder = new VoltProjectBuilder();
          ByteArrayOutputStream capturer = new ByteArrayOutputStream();
          PrintStream capturing = new PrintStream(capturer);
          builder.setCompilerDebugPrintStream(capturing);
          builder.addLiteralSchema(simpleSchema);
          builder.addPartitionInfo("blah", "ival");
          builder.addPartitionInfo("indexed_partitioned_blah", "ival");
          // Note: indexed_replicated_blah is left as a replicated table.
          builder.addStmtProcedure("Insert",
                  // Include lots of filthy whitespace to test output cleanup.
                  "insert                            into\t \tblah values\n\n(? \t ,\t\t\t?)                           ;");
          builder.addProcedure(NondeterministicROProc.class);
          builder.addProcedure(NondeterministicRWProc.class);
          builder.addProcedure(DeterministicRONonSeqProc.class);
          builder.addProcedure(DeterministicROSeqProc.class);
          builder.addProcedure(DeterministicRWProc1.class);
          builder.addProcedure(DeterministicRWProc2.class);
  
          builder.addProcedure(ProcSPcandidate1.class);
          builder.addProcedure(ProcSPcandidate2.class);
          builder.addProcedure(ProcSPcandidate3.class);
          builder.addProcedure(ProcSPcandidate4.class);
          builder.addProcedure(ProcSPcandidate5.class);
          builder.addProcedure(ProcSPcandidate6.class);
          builder.addProcedure(ProcSPcandidate7.class);
  
          builder.addProcedure(ProcSPNoncandidate1.class);
          builder.addProcedure(ProcSPNoncandidate2.class);
          builder.addProcedure(ProcSPNoncandidate3.class);
          builder.addProcedure(ProcSPNoncandidate4.class);
          builder.addProcedure(ProcSPNoncandidate5.class);
          builder.addProcedure(ProcSPNoncandidate6.class);
  
          builder.addStmtProcedure("StmtSPcandidate1", "select count(*) from blah where ival = ?");
          builder.addStmtProcedure("StmtSPcandidate2", "select count(*) from blah where ival = 12345678");
          builder.addStmtProcedure("StmtSPcandidate3",
                                   "select count(*) from blah, indexed_replicated_blah " +
                                   "where indexed_replicated_blah.sval = blah.sval and blah.ival = 12345678");
          builder.addStmtProcedure("StmtSPcandidate4",
                                   "select count(*) from blah, indexed_replicated_blah " +
                                   "where indexed_replicated_blah.sval = blah.sval and blah.ival = abs(1)+1");
          builder.addStmtProcedure("StmtSPcandidate5", "select count(*) from blah where sval = ? and ival = 12345678");
          builder.addStmtProcedure("StmtSPcandidate6", "select count(*) from blah where sval = ? and ival = ?");
          builder.addStmtProcedure("StmtSPNoncandidate1", "select count(*) from blah where sval = ?");
          builder.addStmtProcedure("StmtSPNoncandidate2", "select count(*) from blah where sval = '12345678'");
          builder.addStmtProcedure("StmtSPNoncandidate3", "select count(*) from indexed_replicated_blah where ival = ?");
          builder.addStmtProcedure("FullIndexScan", "select ival, sval from indexed_replicated_blah");
          boolean success = builder.compile(Configuration.getPathToCatalogForTest("annotations.jar"));
          assert(success);
          String captured = capturer.toString("UTF-8");
          System.out.print("\n"
                           + ":----------------------------------------------------------------------:\n"
                           + ":  Start of captured output\n"
                           + ":----------------------------------------------------------------------:\n");
          System.out.println(captured);
          System.out.print("\n"
                          + ":----------------------------------------------------------------------:\n"
                          + ":  End of captured output\n"
                          + ":----------------------------------------------------------------------:\n");
  