@Test
  public void testNestedTransaction() throws Exception {
    TransactionManager transactionManager = createTestTransactionManager();

    Session session =
        sessionBuilder()
            .setTransactionId(TransactionId.create())
            .setClientTransactionSupport()
            .build();
    QueryStateMachine stateMachine =
        QueryStateMachine.begin(
            new QueryId("query"),
            "START TRANSACTION",
            session,
            URI.create("fake://uri"),
            true,
            transactionManager,
            executor);

    try {
      try {
        new StartTransactionTask()
            .execute(
                new StartTransaction(ImmutableList.of()),
                transactionManager,
                metadata,
                new AllowAllAccessControl(),
                stateMachine)
            .join();
        Assert.fail();
      } catch (CompletionException e) {
        throw Throwables.propagate(e.getCause());
      }
    } catch (PrestoException e) {
      Assert.assertEquals(e.getErrorCode(), NOT_SUPPORTED.toErrorCode());
    }
    Assert.assertTrue(transactionManager.getAllTransactionInfos().isEmpty());

    Assert.assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId());
    Assert.assertFalse(
        stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent());
  }
  @Test
  public void testStartTransactionIdleExpiration() throws Exception {
    Session session = sessionBuilder().setClientTransactionSupport().build();
    TransactionManager transactionManager =
        TransactionManager.create(
            new TransactionManagerConfig()
                .setIdleTimeout(new Duration(1, TimeUnit.MICROSECONDS)) // Fast idle timeout
                .setIdleCheckInterval(new Duration(10, TimeUnit.MILLISECONDS)),
            scheduledExecutor,
            executor);
    QueryStateMachine stateMachine =
        QueryStateMachine.begin(
            new QueryId("query"),
            "START TRANSACTION",
            session,
            URI.create("fake://uri"),
            true,
            transactionManager,
            executor);
    Assert.assertFalse(stateMachine.getSession().getTransactionId().isPresent());

    new StartTransactionTask()
        .execute(
            new StartTransaction(ImmutableList.of()),
            transactionManager,
            metadata,
            new AllowAllAccessControl(),
            stateMachine)
        .join();
    Assert.assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId());
    Assert.assertTrue(
        stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent());

    long start = System.nanoTime();
    while (!transactionManager.getAllTransactionInfos().isEmpty()) {
      if (Duration.nanosSince(start).toMillis() > 10_000) {
        Assert.fail("Transaction did not expire in the allotted time");
      }
      TimeUnit.MILLISECONDS.sleep(10);
    }
  }
  @Test
  public void testStartTransactionTooManyIsolationLevels() throws Exception {
    Session session = sessionBuilder().setClientTransactionSupport().build();
    TransactionManager transactionManager = createTestTransactionManager();
    QueryStateMachine stateMachine =
        QueryStateMachine.begin(
            new QueryId("query"),
            "START TRANSACTION",
            session,
            URI.create("fake://uri"),
            true,
            transactionManager,
            executor);
    Assert.assertFalse(stateMachine.getSession().getTransactionId().isPresent());

    try {
      try {
        new StartTransactionTask()
            .execute(
                new StartTransaction(
                    ImmutableList.of(
                        new Isolation(Isolation.Level.READ_COMMITTED),
                        new Isolation(Isolation.Level.READ_COMMITTED))),
                transactionManager,
                metadata,
                new AllowAllAccessControl(),
                stateMachine)
            .join();
        Assert.fail();
      } catch (CompletionException e) {
        throw Throwables.propagate(e.getCause());
      }
    } catch (SemanticException e) {
      Assert.assertEquals(e.getCode(), INVALID_TRANSACTION_MODE);
    }
    Assert.assertTrue(transactionManager.getAllTransactionInfos().isEmpty());

    Assert.assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId());
    Assert.assertFalse(
        stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent());
  }
  @Test
  public void testStartTransaction() throws Exception {
    Session session = sessionBuilder().setClientTransactionSupport().build();
    TransactionManager transactionManager = createTestTransactionManager();
    QueryStateMachine stateMachine =
        QueryStateMachine.begin(
            new QueryId("query"),
            "START TRANSACTION",
            session,
            URI.create("fake://uri"),
            true,
            transactionManager,
            executor);
    Assert.assertFalse(stateMachine.getSession().getTransactionId().isPresent());

    new StartTransactionTask()
        .execute(
            new StartTransaction(ImmutableList.of()),
            transactionManager,
            metadata,
            new AllowAllAccessControl(),
            stateMachine)
        .join();
    Assert.assertFalse(stateMachine.getQueryInfoWithoutDetails().isClearTransactionId());
    Assert.assertTrue(
        stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().isPresent());
    Assert.assertEquals(transactionManager.getAllTransactionInfos().size(), 1);

    TransactionInfo transactionInfo =
        transactionManager.getTransactionInfo(
            stateMachine.getQueryInfoWithoutDetails().getStartedTransactionId().get());
    Assert.assertFalse(transactionInfo.isAutoCommitContext());
  }
Esempio n. 5
0
  @Override
  public CompletableFuture<?> execute(
      Call call,
      TransactionManager transactionManager,
      Metadata metadata,
      AccessControl accessControl,
      QueryStateMachine stateMachine) {
    if (!stateMachine.isAutoCommit()) {
      throw new PrestoException(
          NOT_SUPPORTED, "Procedures cannot be called within a transaction (use autocommit mode)");
    }

    Session session = stateMachine.getSession();
    QualifiedObjectName procedureName = createQualifiedObjectName(session, call, call.getName());
    Procedure procedure = metadata.getProcedureRegistry().resolve(procedureName);

    // map declared argument names to positions
    Map<String, Integer> positions = new HashMap<>();
    for (int i = 0; i < procedure.getArguments().size(); i++) {
      positions.put(procedure.getArguments().get(i).getName(), i);
    }

    // per specification, do not allow mixing argument types
    Predicate<CallArgument> hasName = argument -> argument.getName().isPresent();
    boolean anyNamed = call.getArguments().stream().anyMatch(hasName);
    boolean allNamed = call.getArguments().stream().allMatch(hasName);
    if (anyNamed && !allNamed) {
      throw new SemanticException(
          INVALID_PROCEDURE_ARGUMENTS, call, "Named and positional arguments cannot be mixed");
    }

    // get the argument names in call order
    Map<String, CallArgument> names = new LinkedHashMap<>();
    for (int i = 0; i < call.getArguments().size(); i++) {
      CallArgument argument = call.getArguments().get(i);
      if (argument.getName().isPresent()) {
        String name = argument.getName().get();
        if (names.put(name, argument) != null) {
          throw new SemanticException(
              INVALID_PROCEDURE_ARGUMENTS, argument, "Duplicate procedure argument: %s", name);
        }
        if (!positions.containsKey(name)) {
          throw new SemanticException(
              INVALID_PROCEDURE_ARGUMENTS, argument, "Unknown argument name: %s", name);
        }
      } else if (i < procedure.getArguments().size()) {
        names.put(procedure.getArguments().get(i).getName(), argument);
      } else {
        throw new SemanticException(
            INVALID_PROCEDURE_ARGUMENTS, call, "Too many arguments for procedure");
      }
    }

    // verify argument count
    if (names.size() < positions.size()) {
      throw new SemanticException(
          INVALID_PROCEDURE_ARGUMENTS, call, "Too few arguments for procedure");
    }

    // get argument values
    Object[] values = new Object[procedure.getArguments().size()];
    for (Entry<String, CallArgument> entry : names.entrySet()) {
      CallArgument callArgument = entry.getValue();
      int index = positions.get(entry.getKey());
      Argument argument = procedure.getArguments().get(index);

      Expression expression = callArgument.getValue();
      Type type = argument.getType();

      Object value = evaluateConstantExpression(expression, type, metadata, session);

      values[index] = toTypeObjectValue(session, type, value);
    }

    // validate arguments
    MethodType methodType = procedure.getMethodHandle().type();
    for (int i = 0; i < procedure.getArguments().size(); i++) {
      if ((values[i] == null) && methodType.parameterType(i).isPrimitive()) {
        String name = procedure.getArguments().get(i).getName();
        throw new PrestoException(
            INVALID_PROCEDURE_ARGUMENT, "Procedure argument cannot be null: " + name);
      }
    }

    // insert session argument
    List<Object> arguments = new ArrayList<>();
    Iterator<Object> valuesIterator = asList(values).iterator();
    for (Class<?> type : methodType.parameterList()) {
      if (ConnectorSession.class.isAssignableFrom(type)) {
        arguments.add(session.toConnectorSession(procedureName.getCatalogName()));
      } else {
        arguments.add(valuesIterator.next());
      }
    }

    try {
      procedure.getMethodHandle().invokeWithArguments(arguments);
    } catch (Throwable t) {
      if (t instanceof InterruptedException) {
        Thread.currentThread().interrupt();
      }
      propagateIfInstanceOf(t, PrestoException.class);
      throw new PrestoException(PROCEDURE_CALL_FAILED, t);
    }

    return completedFuture(null);
  }