Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/_docs/SQL/sql-calcite.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ The new SQL engine mostly inherits data manipulation language (DML) statements s

In most cases, statement syntax is compliant with the old SQL engine. But there are still some differences between DML dialects in H2-based engine and Calcite-based engine. For example, note the `MERGE` statement syntax has changed.

=== Transactions

The Calcite-based SQL engine supports SQL savepoint commands for explicit transactions. See link:sql-reference/transactions[Transactions, window=_blank] for syntax and usage details.

=== Supported Functions

The Calcite-based SQL engine currently supports:
Expand Down
76 changes: 76 additions & 0 deletions docs/_docs/sql-reference/transactions.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
= Transactions

This page describes SQL transaction commands supported by the Calcite-based SQL engine.

== SAVEPOINT

Creates a named savepoint in the current transaction.

[source,sql]
----
SAVEPOINT savepointName
----

=== Parameters

- `savepointName` - the name of the savepoint to create.

=== Description

`SAVEPOINT` can be used only inside an explicit `PESSIMISTIC` transaction.

The command records the current transaction state. Later, you can use `ROLLBACK TO SAVEPOINT` to roll back all transaction changes made after the savepoint was created.

If a savepoint with the same name already exists, `SAVEPOINT` replaces it. The command does not roll back any transaction changes. It removes the previous savepoint with that name and makes the name refer to the current transaction state.

== ROLLBACK TO SAVEPOINT

Rolls back transaction changes to a previously created savepoint.

[source,sql]
----
ROLLBACK TO SAVEPOINT savepointName
----

=== Parameters

- `savepointName` - the name of the savepoint to roll back to.

=== Description

`ROLLBACK TO SAVEPOINT` can be used only inside an explicit `PESSIMISTIC` transaction.

The command rolls back all transaction changes made after the specified savepoint was created. The transaction remains active and can be committed or rolled back later.

When a transaction is rolled back to a savepoint, savepoints created after the target savepoint are released. The target savepoint remains available and can be used again.

If the specified savepoint does not exist, the command fails.

== Example

[source,sql]
----
INSERT INTO Person(id, name) VALUES (1, 'John');
Comment thread
vldpyatkov marked this conversation as resolved.

SAVEPOINT before_update;

UPDATE Person SET name = 'Jane' WHERE id = 1;

ROLLBACK TO SAVEPOINT before_update;
----

After `ROLLBACK TO SAVEPOINT before_update`, the `UPDATE` statement is rolled back, but the preceding `INSERT` statement remains part of the active transaction.
2 changes: 2 additions & 0 deletions modules/calcite/src/main/codegen/config.fmpp
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ data: {
"SqlKillTransaction()",
"SqlKillComputeTask()",
"SqlKillQuery()",
"SqlSavepoint()",
"SqlRollbackToSavepoint()",
"SqlCommitTransaction()",
"SqlRollbackTransaction()"
"SqlStatisticsAnalyze()"
Expand Down
22 changes: 22 additions & 0 deletions modules/calcite/src/main/codegen/includes/parserImpls.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,28 @@ SqlNode SqlRollbackTransaction():
}
}

SqlNode SqlSavepoint():
{
final Span s;
final SqlIdentifier name;
}
{
<SAVEPOINT> { s = span(); } name = SimpleIdentifier() {
return new IgniteSqlSavepoint(s.end(this), name);
}
}

SqlNode SqlRollbackToSavepoint():
{
final Span s;
final SqlIdentifier name;
}
{
<ROLLBACK> { s = span(); } <TO> <SAVEPOINT> name = SimpleIdentifier() {
return new IgniteSqlRollbackToSavepoint(s.end(this), name);
}
}

IgniteSqlStatisticsTable StatisticsTable():
{
final Span s = Span.of();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ private FragmentPlan prepareFragment(BaseQueryContext ctx, String jsonFragment)
/** */
private FieldsQueryCursor<List<?>> executeDdl(RootQuery<Row> qry, DdlPlan plan) {
try {
ddlCmdHnd.handle(qry.id(), plan.command());
ddlCmdHnd.handle(qry.id(), qry.context(), plan.command());
}
catch (IgniteCheckedException e) {
throw new IgniteSQLException("Failed to execute DDL statement [stmt=" + qry.sql() +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Table;
import org.apache.ignite.IgniteCheckedException;
Expand All @@ -36,13 +37,15 @@
import org.apache.ignite.internal.processors.cache.GridCacheContext;
import org.apache.ignite.internal.processors.cache.GridCacheContextInfo;
import org.apache.ignite.internal.processors.cache.GridCacheProcessor;
import org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal;
import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode;
import org.apache.ignite.internal.processors.query.GridQueryProcessor;
import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor;
import org.apache.ignite.internal.processors.query.IgniteSQLException;
import org.apache.ignite.internal.processors.query.QueryEntityEx;
import org.apache.ignite.internal.processors.query.QueryField;
import org.apache.ignite.internal.processors.query.QueryUtils;
import org.apache.ignite.internal.processors.query.calcite.prepare.BaseQueryContext;
import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.AlterTableAddCommand;
import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.AlterTableDropCommand;
import org.apache.ignite.internal.processors.query.calcite.prepare.ddl.ColumnDefinition;
Expand All @@ -62,6 +65,7 @@
import org.apache.ignite.lang.IgniteUuid;
import org.apache.ignite.plugin.security.SecurityPermission;

import static org.apache.ignite.internal.processors.cache.distributed.near.GridNearTxLocal.SAVEPOINTS_EXPLICIT_TX_ONLY;
import static org.apache.ignite.internal.processors.query.QueryUtils.convert;
import static org.apache.ignite.internal.processors.query.QueryUtils.isDdlOnSchemaSupported;

Expand Down Expand Up @@ -97,12 +101,14 @@ public DdlCommandHandler(GridQueryProcessor qryProc, GridCacheProcessor cachePro
}

/** */
public void handle(UUID qryId, DdlCommand cmd) throws IgniteCheckedException {
public void handle(UUID qryId, BaseQueryContext qryCtx, DdlCommand cmd) throws IgniteCheckedException {
try {
if (cmd instanceof TransactionCommand)
return;
if (cmd instanceof TransactionCommand) {
GridNearTxLocal tx = Commons.queryTransaction(qryCtx, cacheProc.context());

if (cmd instanceof CreateTableCommand)
handle0((TransactionCommand)cmd, tx);
}
else if (cmd instanceof CreateTableCommand)
handle0((CreateTableCommand)cmd);
else if (cmd instanceof DropTableCommand)
handle0((DropTableCommand)cmd);
Expand All @@ -123,6 +129,36 @@ else if (cmd instanceof NativeCommandWrapper)
}
}

/**
* Handles transaction control commands (SAVEPOINT and ROLLBACK TO SAVEPOINT).
*
* @param cmd Command.
* @param tx Transaction. Can be null if there is no transaction associated with the query context.
* @throws IgniteCheckedException If failed to execute the command.
*/
private void handle0(TransactionCommand cmd, @Nullable GridNearTxLocal tx) throws IgniteCheckedException {
if (cmd.type() == TransactionCommand.Type.NOOP)
Comment thread
zstan marked this conversation as resolved.
return;

if (tx == null)
throw new IgniteSQLException(SAVEPOINTS_EXPLICIT_TX_ONLY, IgniteQueryErrorCode.UNSUPPORTED_OPERATION);

switch (cmd.type()) {
case SAVEPOINT:
tx.savepoint(cmd.savepointName(), true);

break;

case ROLLBACK_TO_SAVEPOINT:
tx.rollbackToSavepoint(cmd.savepointName());

break;

default:
throw new AssertionError("Unexpected transaction command type: " + cmd.type());
}
}

/** */
private void handle0(CreateTableCommand cmd) throws IgniteCheckedException {
security.authorize(cmd.cacheName(), SecurityPermission.CACHE_CREATE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOption;
import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlCreateTableOptionEnum;
import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlRollback;
import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlRollbackToSavepoint;
import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlSavepoint;
import org.apache.ignite.internal.processors.query.calcite.type.OtherType;
import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils;
import org.apache.ignite.internal.util.typedef.F;
Expand Down Expand Up @@ -169,6 +171,18 @@ public DdlCommand convert(SqlDdl ddlNode, PlanningContext ctx) {
if (ddlNode instanceof IgniteSqlCommit || ddlNode instanceof IgniteSqlRollback)
return new TransactionCommand();

if (ddlNode instanceof IgniteSqlSavepoint) {
return new TransactionCommand(
TransactionCommand.Type.SAVEPOINT,
((IgniteSqlSavepoint)ddlNode).name().getSimple());
}

if (ddlNode instanceof IgniteSqlRollbackToSavepoint) {
return new TransactionCommand(
TransactionCommand.Type.ROLLBACK_TO_SAVEPOINT,
((IgniteSqlRollbackToSavepoint)ddlNode).name().getSimple());
}

if (SqlToNativeCommandConverter.isSupported(ddlNode))
return SqlToNativeCommandConverter.convert(ddlNode, ctx);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,49 @@
*/
package org.apache.ignite.internal.processors.query.calcite.prepare.ddl;

/**
* No-op command for COMMIT and ROLLBACK
*/
import org.jetbrains.annotations.Nullable;

/** Command for transaction control statements. */
public class TransactionCommand implements DdlCommand {
/** Transaction command type. */
public enum Type {
/** No-op command for COMMIT and ROLLBACK. */
NOOP,

/** Create a transaction savepoint. */
SAVEPOINT,

/** Roll transaction changes back to a savepoint. */
ROLLBACK_TO_SAVEPOINT
}

/** */
private final Type type;

/** Savepoint name. */
@Nullable private final String savepointName;

/** */
public TransactionCommand() {
this(Type.NOOP, null);
}

/**
* @param type Command type.
* @param savepointName Savepoint name.
*/
public TransactionCommand(Type type, String savepointName) {
this.type = type;
this.savepointName = savepointName;
}

/** */
public Type type() {
return type;
}

/** */
public String savepointName() {
return savepointName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.ignite.internal.processors.query.calcite.sql;

import java.util.List;
import org.apache.calcite.sql.SqlDdl;
import org.apache.calcite.sql.SqlIdentifier;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlOperator;
import org.apache.calcite.sql.SqlSpecialOperator;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.parser.SqlParserPos;

/** Parse tree for {@code ROLLBACK TO SAVEPOINT name} statement. */
public class IgniteSqlRollbackToSavepoint extends SqlDdl {
/** */
protected static final SqlOperator OPERATOR =
new SqlSpecialOperator("ROLLBACK TO SAVEPOINT", SqlKind.OTHER_DDL);
Comment thread
zstan marked this conversation as resolved.

/** Savepoint name. */
private final SqlIdentifier name;

/**
* @param pos Parser position.
* @param name Savepoint name.
*/
public IgniteSqlRollbackToSavepoint(SqlParserPos pos, SqlIdentifier name) {
super(OPERATOR, pos);

this.name = name;
}

/** */
public SqlIdentifier name() {
return name;
}

/** {@inheritDoc} */
@Override public List<SqlNode> getOperandList() {
return List.of(name);
}

/** {@inheritDoc} */
@Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
writer.keyword(getOperator().getName());
name.unparse(writer, leftPrec, rightPrec);
}
}
Loading
Loading