Repository: ignite Updated Branches: refs/heads/master 8f327a18b -> ec290234b
IGNITE-4490: SQL: avoid querying H2 for INSERT and MERGE when it is not needed. This closes #1387. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/ec290234 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/ec290234 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/ec290234 Branch: refs/heads/master Commit: ec290234bf7b54c5549bd7aa0d99b36d3d0638ac Parents: 8f327a1 Author: Alexander Paschenko <[email protected]> Authored: Tue Dec 19 11:11:11 2017 +0300 Committer: devozerov <[email protected]> Committed: Tue Dec 19 11:11:11 2017 +0300 ---------------------------------------------------------------------- .../ignite/jdbc/JdbcErrorsAbstractSelfTest.java | 2 +- .../ignite/jdbc/thin/JdbcThinBatchSelfTest.java | 5 +- .../query/h2/DmlStatementsProcessor.java | 53 +++++----- .../processors/query/h2/dml/DmlArgument.java | 31 ++++++ .../processors/query/h2/dml/DmlArguments.java | 104 +++++++++++++++++++ .../processors/query/h2/dml/DmlAstUtils.java | 14 ++- .../processors/query/h2/dml/DmlUtils.java | 19 ++-- .../processors/query/h2/dml/FastUpdate.java | 91 ++-------------- .../query/h2/dml/FastUpdateArgument.java | 27 ----- .../processors/query/h2/dml/UpdatePlan.java | 80 +++++++++++--- .../query/h2/dml/UpdatePlanBuilder.java | 37 ++++++- 11 files changed, 304 insertions(+), 159 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java index 952baa5..fb96f31 100644 --- a/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/JdbcErrorsAbstractSelfTest.java @@ -107,7 +107,7 @@ public abstract class JdbcErrorsAbstractSelfTest extends GridCommonAbstractTest public void testDmlErrors() throws SQLException { checkErrorState("INSERT INTO \"test\".INTEGER(_key, _val) values(1, null)", "22004"); - checkErrorState("INSERT INTO \"test\".INTEGER(_key, _val) values(1, 'zzz')", "50000"); + checkErrorState("INSERT INTO \"test\".INTEGER(_key, _val) values(1, 'zzz')", "0700B"); } /** http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java ---------------------------------------------------------------------- diff --git a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java index 5e2e39e..8609615 100644 --- a/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java +++ b/modules/clients/src/test/java/org/apache/ignite/jdbc/thin/JdbcThinBatchSelfTest.java @@ -260,7 +260,8 @@ public class JdbcThinBatchSelfTest extends JdbcThinAbstractDmlStatementSelfTest pstmt.executeBatch(); fail("BatchUpdateException must be thrown"); - } catch(BatchUpdateException e) { + } + catch(BatchUpdateException e) { int [] updCnts = e.getUpdateCounts(); assertEquals("Invalid update counts size", BATCH_SIZE, updCnts.length); @@ -268,7 +269,7 @@ public class JdbcThinBatchSelfTest extends JdbcThinAbstractDmlStatementSelfTest for (int i = 0; i < BATCH_SIZE; ++i) assertEquals("Invalid update count",1, updCnts[i]); - if (!e.getMessage().contains("Failed to execute SQL query.")) { + if (!e.getMessage().contains("Value conversion failed")) { log.error("Invalid exception: ", e); fail(); http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java index 243d1dc..9a6b0af 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/DmlStatementsProcessor.java @@ -32,7 +32,6 @@ import java.util.concurrent.TimeUnit; import javax.cache.processor.EntryProcessor; import javax.cache.processor.EntryProcessorException; import javax.cache.processor.MutableEntry; - import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteDataStreamer; import org.apache.ignite.IgniteException; @@ -51,7 +50,6 @@ import org.apache.ignite.internal.processors.query.GridQueryFieldsResultAdapter; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.h2.dml.DmlBatchSender; import org.apache.ignite.internal.processors.query.h2.dml.DmlDistributedPlanInfo; -import org.apache.ignite.internal.processors.query.h2.dml.FastUpdate; import org.apache.ignite.internal.processors.query.h2.dml.UpdateMode; import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlan; import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlanBuilder; @@ -167,7 +165,7 @@ public class DmlStatementsProcessor { UpdateResult r; try { - r = executeUpdateStatement(schemaName, cctx, conn, prepared, fieldsQry, loc, filters, cancel, errKeys); + r = executeUpdateStatement(schemaName, cctx, conn, prepared, fieldsQry, loc, filters, cancel); } finally { cctx.operationContextPerCall(opCtx); @@ -247,15 +245,13 @@ public class DmlStatementsProcessor { * @throws IgniteCheckedException if failed. */ @SuppressWarnings({"unchecked", "ConstantConditions"}) - long streamUpdateQuery(IgniteDataStreamer streamer, PreparedStatement stmt, Object[] args) + long streamUpdateQuery(IgniteDataStreamer streamer, PreparedStatement stmt, final Object[] args) throws IgniteCheckedException { - args = U.firstNotNull(args, X.EMPTY_OBJECT_ARRAY); - Prepared p = GridSqlQueryParser.prepared(stmt); assert p != null; - UpdatePlan plan = UpdatePlanBuilder.planForStatement(p, true, idx, null, null, null); + final UpdatePlan plan = UpdatePlanBuilder.planForStatement(p, true, idx, null, null, null); if (!F.eq(streamer.cacheName(), plan.cacheContext().name())) throw new IgniteSQLException("Cross cache streaming is not supported, please specify cache explicitly" + @@ -270,14 +266,22 @@ public class DmlStatementsProcessor { final ArrayList<List<?>> data = new ArrayList<>(plan.rowCount()); - final GridQueryFieldsResult res = idx.queryLocalSqlFields(idx.schema(cctx.name()), plan.selectQuery(), - F.asList(args), null, false, 0, null); - QueryCursorImpl<List<?>> stepCur = new QueryCursorImpl<>(new Iterable<List<?>>() { @Override public Iterator<List<?>> iterator() { try { - return new GridQueryCacheObjectsIterator(res.iterator(), idx.objectContext(), - cctx.keepBinary()); + Iterator<List<?>> it; + + if (!F.isEmpty(plan.selectQuery())) { + GridQueryFieldsResult res = idx.queryLocalSqlFields(idx.schema(cctx.name()), + plan.selectQuery(), F.asList(U.firstNotNull(args, X.EMPTY_OBJECT_ARRAY)), + null, false, 0, null); + + it = res.iterator(); + } + else + it = plan.createRows(U.firstNotNull(args, X.EMPTY_OBJECT_ARRAY)).iterator(); + + return new GridQueryCacheObjectsIterator(it, idx.objectContext(), cctx.keepBinary()); } catch (IgniteCheckedException e) { throw new IgniteException(e); @@ -329,27 +333,23 @@ public class DmlStatementsProcessor { * @param loc Local query flag. * @param filters Cache name and key filter. * @param cancel Query cancel state holder. - * @param failedKeys Keys to restrict UPDATE and DELETE operations with. Null or empty array means no restriction. * @return Pair [number of successfully processed items; keys that have failed to be processed] * @throws IgniteCheckedException if failed. */ @SuppressWarnings({"ConstantConditions", "unchecked"}) private UpdateResult executeUpdateStatement(String schemaName, final GridCacheContext cctx, Connection c, - Prepared prepared, SqlFieldsQuery fieldsQry, boolean loc, IndexingQueryFilter filters, - GridQueryCancel cancel, Object[] failedKeys) throws IgniteCheckedException { + Prepared prepared, SqlFieldsQuery fieldsQry, boolean loc, IndexingQueryFilter filters, GridQueryCancel cancel) + throws IgniteCheckedException { int mainCacheId = cctx.cacheId(); Integer errKeysPos = null; UpdatePlan plan = getPlanForStatement(schemaName, c, prepared, fieldsQry, loc, errKeysPos); - FastUpdate fastUpdate = plan.fastUpdate(); - - if (fastUpdate != null) { - assert F.isEmpty(failedKeys) && errKeysPos == null; + UpdateResult fastUpdateRes = plan.processFast(fieldsQry.getArgs()); - return fastUpdate.execute(plan.cacheContext().cache(), fieldsQry.getArgs()); - } + if (fastUpdateRes != null) + return fastUpdateRes; if (plan.distributedPlan() != null) { UpdateResult result = doDistributedUpdate(schemaName, fieldsQry, plan, cancel); @@ -359,13 +359,13 @@ public class DmlStatementsProcessor { return result; } - assert !F.isEmpty(plan.selectQuery()); - - QueryCursorImpl<List<?>> cur; + Iterable<List<?>> cur; // Do a two-step query only if locality flag is not set AND if plan's SELECT corresponds to an actual // sub-query and not some dummy stuff like "select 1, 2, 3;" if (!loc && !plan.isLocalSubquery()) { + assert !F.isEmpty(plan.selectQuery()); + SqlFieldsQuery newFieldsQry = new SqlFieldsQuery(plan.selectQuery(), fieldsQry.isCollocated()) .setArgs(fieldsQry.getArgs()) .setDistributedJoins(fieldsQry.isDistributedJoins()) @@ -374,9 +374,10 @@ public class DmlStatementsProcessor { .setPageSize(fieldsQry.getPageSize()) .setTimeout(fieldsQry.getTimeout(), TimeUnit.MILLISECONDS); - cur = (QueryCursorImpl<List<?>>)idx.queryDistributedSqlFields(schemaName, newFieldsQry, true, - cancel, mainCacheId, true).get(0); + cur = idx.queryDistributedSqlFields(schemaName, newFieldsQry, true, cancel, mainCacheId, true).get(0); } + else if (plan.hasRows()) + cur = plan.createRows(fieldsQry.getArgs()); else { final GridQueryFieldsResult res = idx.queryLocalSqlFields(schemaName, plan.selectQuery(), F.asList(fieldsQry.getArgs()), filters, fieldsQry.isEnforceJoinOrder(), fieldsQry.getTimeout(), cancel); http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArgument.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArgument.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArgument.java new file mode 100644 index 0000000..b3c3dce --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArgument.java @@ -0,0 +1,31 @@ +/* + * 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.h2.dml; + +/** + * DML argument + */ +public interface DmlArgument { + /** + * Get argument from parameter list. + * + * @param params Query input parameters. + * @return value. + */ + Object get(Object[] params); +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArguments.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArguments.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArguments.java new file mode 100644 index 0000000..f5aa332 --- /dev/null +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlArguments.java @@ -0,0 +1,104 @@ +/* + * 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.h2.dml; + +import org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst; +import org.apache.ignite.internal.processors.query.h2.sql.GridSqlElement; +import org.apache.ignite.internal.processors.query.h2.sql.GridSqlParameter; +import org.jetbrains.annotations.Nullable; + +/** + * DML arguments factory. + */ +public class DmlArguments { + /** Operand that always evaluates as {@code null}. */ + private final static DmlArgument NULL_ARG = new ConstantArgument(null); + + /** + * Create argument from AST element. + * + * @param el Element. + * @return DML argument. + */ + public static DmlArgument create(@Nullable GridSqlElement el) { + assert el == null ^ (el instanceof GridSqlConst || el instanceof GridSqlParameter); + + if (el == null) + return NULL_ARG; + + if (el instanceof GridSqlConst) + return new ConstantArgument(((GridSqlConst)el).value().getObject()); + else + return new ParamArgument(((GridSqlParameter)el).index()); + } + + /** + * Private constructor. + */ + private DmlArguments() { + // No-op. + } + + /** + * Value argument. + */ + private static class ConstantArgument implements DmlArgument { + /** Value to return. */ + private final Object val; + + /** + * Constructor. + * + * @param val Value. + */ + private ConstantArgument(Object val) { + this.val = val; + } + + /** {@inheritDoc} */ + public Object get(Object[] params) { + return val; + } + } + + /** + * Parameter argument. + */ + private static class ParamArgument implements DmlArgument { + /** Value to return. */ + private final int paramIdx; + + /** + * Constructor. + * + * @param paramIdx Parameter index. + */ + private ParamArgument(int paramIdx) { + assert paramIdx >= 0; + + this.paramIdx = paramIdx; + } + + /** {@inheritDoc} */ + @Override public Object get(Object[] params) { + assert params.length > paramIdx; + + return params[paramIdx]; + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java index 054e708..161ff4a 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlAstUtils.java @@ -83,7 +83,7 @@ public final class DmlAstUtils { * @param cols Columns to insert values into. * @param rows Rows to create pseudo-SELECT upon. * @param subQry Subquery to use rather than rows. - * @return Subquery or pseudo-SELECT to evaluate inserted expressions. + * @return Subquery or pseudo-SELECT to evaluate inserted expressions, or {@code null} no query needs to be run. */ public static GridSqlQuery selectForInsertOrMerge(GridSqlColumn[] cols, List<GridSqlElement[]> rows, GridSqlQuery subQry) { @@ -98,6 +98,8 @@ public final class DmlAstUtils { GridSqlArray[] args = new GridSqlArray[cols.length]; + boolean noQry = true; + for (int i = 0; i < cols.length; i++) { GridSqlArray arr = new GridSqlArray(rows.size()); @@ -121,10 +123,18 @@ public final class DmlAstUtils { for (GridSqlElement[] row : rows) { assert cols.length == row.length; - for (int i = 0; i < row.length; i++) + for (int i = 0; i < row.length; i++) { + GridSqlElement el = row[i]; + + noQry &= (el instanceof GridSqlConst || el instanceof GridSqlParameter); + args[i].addChild(row[i]); + } } + if (noQry) + return null; + return sel; } else { http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlUtils.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlUtils.java index 6621fc2..8d4861e 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlUtils.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/DmlUtils.java @@ -17,6 +17,10 @@ package org.apache.ignite.internal.processors.query.h2.dml; +import java.lang.reflect.Array; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Date; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.processors.cache.query.IgniteQueryErrorCode; import org.apache.ignite.internal.processors.query.IgniteSQLException; @@ -30,11 +34,6 @@ import org.h2.value.ValueDate; import org.h2.value.ValueTime; import org.h2.value.ValueTimestamp; -import java.lang.reflect.Array; -import java.sql.Time; -import java.sql.Timestamp; -import java.util.Date; - /** * DML utility methods. */ @@ -101,7 +100,15 @@ public class DmlUtils { return newArr; } - return H2Utils.convert(val, desc, type); + Object res = H2Utils.convert(val, desc, type); + + if (res instanceof Date && res.getClass() != Date.class && expCls == Date.class) { + // We can get a Timestamp instead of Date when converting a String to Date + // without query - let's handle this + return new Date(((Date) res).getTime()); + } + + return res; } catch (Exception e) { throw new IgniteSQLException("Value conversion failed [from=" + currCls.getName() + ", to=" + http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java index e662245..dcceff3 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdate.java @@ -20,26 +20,21 @@ package org.apache.ignite.internal.processors.query.h2.dml; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.internal.processors.cache.GridCacheAdapter; import org.apache.ignite.internal.processors.query.h2.UpdateResult; -import org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlElement; -import org.apache.ignite.internal.processors.query.h2.sql.GridSqlParameter; import org.jetbrains.annotations.Nullable; /** * Arguments for fast, query-less UPDATE or DELETE - key and, optionally, value and new value. */ public final class FastUpdate { - /** Operand that always evaluates as {@code null}. */ - private final static FastUpdateArgument NULL_ARG = new ConstantArgument(null); - /** Operand to compute key. */ - private final FastUpdateArgument keyArg; + private final DmlArgument keyArg; /** Operand to compute value. */ - private final FastUpdateArgument valArg; + private final DmlArgument valArg; /** Operand to compute new value. */ - private final FastUpdateArgument newValArg; + private final DmlArgument newValArg; /** * Create fast update instance. @@ -50,9 +45,9 @@ public final class FastUpdate { * @return Fast update. */ public static FastUpdate create(GridSqlElement key, GridSqlElement val, @Nullable GridSqlElement newVal) { - FastUpdateArgument keyArg = argument(key); - FastUpdateArgument valArg = argument(val); - FastUpdateArgument newValArg = argument(newVal); + DmlArgument keyArg = DmlArguments.create(key); + DmlArgument valArg = DmlArguments.create(val); + DmlArgument newValArg = DmlArguments.create(newVal); return new FastUpdate(keyArg, valArg, newValArg); } @@ -64,7 +59,7 @@ public final class FastUpdate { * @param valArg Value argument. * @param newValArg New value argument. */ - private FastUpdate(FastUpdateArgument keyArg, FastUpdateArgument valArg, FastUpdateArgument newValArg) { + private FastUpdate(DmlArgument keyArg, DmlArgument valArg, DmlArgument newValArg) { this.keyArg = keyArg; this.valArg = valArg; this.newValArg = newValArg; @@ -80,12 +75,12 @@ public final class FastUpdate { */ @SuppressWarnings({"unchecked", "ConstantConditions"}) public UpdateResult execute(GridCacheAdapter cache, Object[] args) throws IgniteCheckedException { - Object key = keyArg.apply(args); + Object key = keyArg.get(args); assert key != null; - Object val = valArg.apply(args); - Object newVal = newValArg.apply(args); + Object val = valArg.get(args); + Object newVal = newValArg.get(args); boolean res; @@ -106,70 +101,4 @@ public final class FastUpdate { return res ? UpdateResult.ONE : UpdateResult.ZERO; } - - /** - * Create argument for AST element. - * - * @param el Element. - * @return Argument. - */ - private static FastUpdateArgument argument(@Nullable GridSqlElement el) { - assert el == null ^ (el instanceof GridSqlConst || el instanceof GridSqlParameter); - - if (el == null) - return NULL_ARG; - - if (el instanceof GridSqlConst) - return new ConstantArgument(((GridSqlConst)el).value().getObject()); - else - return new ParamArgument(((GridSqlParameter)el).index()); - } - - /** - * Value argument. - */ - private static class ConstantArgument implements FastUpdateArgument { - /** Value to return. */ - private final Object val; - - /** - * Constructor. - * - * @param val Value. - */ - private ConstantArgument(Object val) { - this.val = val; - } - - /** {@inheritDoc} */ - @Override public Object apply(Object[] arg) throws IgniteCheckedException { - return val; - } - } - - /** - * Parameter argument. - */ - private static class ParamArgument implements FastUpdateArgument { - /** Value to return. */ - private final int paramIdx; - - /** - * Constructor. - * - * @param paramIdx Parameter index. - */ - private ParamArgument(int paramIdx) { - assert paramIdx >= 0; - - this.paramIdx = paramIdx; - } - - /** {@inheritDoc} */ - @Override public Object apply(Object[] arg) throws IgniteCheckedException { - assert arg.length > paramIdx; - - return arg[paramIdx]; - } - } } http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArgument.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArgument.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArgument.java deleted file mode 100644 index dc90fe9..0000000 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArgument.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.h2.dml; - -import org.apache.ignite.internal.util.lang.GridPlainClosure; - -/** - * Operand for fast UPDATE or DELETE (single item operation that does not involve any SELECT). - */ -public interface FastUpdateArgument extends GridPlainClosure<Object[], Object> { - // No-op. -} http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java index 31dc52d..6a45c3c 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlan.java @@ -17,6 +17,10 @@ package org.apache.ignite.internal.processors.query.h2.dml; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.binary.BinaryObject; import org.apache.ignite.binary.BinaryObjectBuilder; @@ -26,6 +30,7 @@ import org.apache.ignite.internal.processors.query.GridQueryProperty; import org.apache.ignite.internal.processors.query.GridQueryTypeDescriptor; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.QueryUtils; +import org.apache.ignite.internal.processors.query.h2.UpdateResult; import org.apache.ignite.internal.processors.query.h2.opt.GridH2RowDescriptor; import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table; import org.apache.ignite.internal.util.typedef.F; @@ -34,10 +39,6 @@ import org.apache.ignite.lang.IgniteBiTuple; import org.h2.table.Column; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - import static org.apache.ignite.internal.processors.query.h2.opt.GridH2KeyValueRowOnheap.DEFAULT_COLUMNS_COUNT; /** @@ -47,7 +48,7 @@ public final class UpdatePlan { /** Initial statement to drive the rest of the logic. */ private final UpdateMode mode; - /** Target table to be affected by initial DML statement. */ + /** to be affected by initial DML statement. */ private final GridH2Table tbl; /** Column names to set or update. */ @@ -74,6 +75,9 @@ public final class UpdatePlan { /** Subquery flag - {@code true} if {@link #selectQry} is an actual subquery that retrieves data from some cache. */ private final boolean isLocSubqry; + /** Rows for query-less MERGE or INSERT. */ + private final List<List<DmlArgument>> rows; + /** Number of rows in rows based MERGE or INSERT. */ private final int rowsNum; @@ -96,6 +100,7 @@ public final class UpdatePlan { * @param valColIdx value column index. * @param selectQry Select query. * @param isLocSubqry Local subquery flag. + * @param rows Rows for query-less INSERT or MERGE. * @param rowsNum Rows number. * @param fastUpdate Fast update (if any). * @param distributed Distributed plan (if any) @@ -111,12 +116,14 @@ public final class UpdatePlan { int valColIdx, String selectQry, boolean isLocSubqry, + List<List<DmlArgument>> rows, int rowsNum, @Nullable FastUpdate fastUpdate, @Nullable DmlDistributedPlanInfo distributed ) { this.colNames = colNames; this.colTypes = colTypes; + this.rows = rows; this.rowsNum = rowsNum; assert mode != null; @@ -161,6 +168,7 @@ public final class UpdatePlan { -1, selectQry, false, + null, 0, fastUpdate, distributed @@ -339,6 +347,61 @@ public final class UpdatePlan { } /** + * Process fast DML operation if possible. + * + * @param args QUery arguments. + * @return Update result or {@code null} if fast update is not applicable for plan. + * @throws IgniteCheckedException If failed. + */ + public UpdateResult processFast(Object[] args) throws IgniteCheckedException { + if (fastUpdate != null) + return fastUpdate.execute(cacheContext().cache(), args); + + return null; + } + + /** + * @return {@code True} if predefined rows exist. + */ + public boolean hasRows() { + return !F.isEmpty(rows); + } + + /** + * Extract rows from plan without performing any query. + * @param args Original query arguments. + * @return Rows from plan. + * @throws IgniteCheckedException if failed. + */ + public List<List<?>> createRows(Object[] args) throws IgniteCheckedException { + assert rowsNum > 0 && !F.isEmpty(colNames); + + List<List<?>> res = new ArrayList<>(rowsNum); + + GridH2RowDescriptor desc = tbl.rowDescriptor(); + + for (List<DmlArgument> row : rows) { + List<Object> resRow = new ArrayList<>(); + + for (int j = 0; j < colNames.length; j++) { + Object colVal = row.get(j).get(args); + + if (j == keyColIdx || j == valColIdx) { + Class<?> colCls = j == keyColIdx ? desc.type().keyClass() : desc.type().valueClass(); + + colVal = DmlUtils.convert(colVal, desc, colCls, colTypes[j]); + } + + resRow.add(colVal); + } + + res.add(resRow); + } + + return res; + } + + /** * @return Update mode. */ public UpdateMode mode() { @@ -379,11 +442,4 @@ public final class UpdatePlan { @Nullable public boolean isLocalSubquery() { return isLocSubqry; } - - /** - * @return Fast update. - */ - @Nullable public FastUpdate fastUpdate() { - return fastUpdate; - } } http://git-wip-us.apache.org/repos/asf/ignite/blob/ec290234/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java index a551639..5ffd264 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/UpdatePlanBuilder.java @@ -21,6 +21,7 @@ import java.lang.reflect.Constructor; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -127,6 +128,8 @@ public final class UpdatePlanBuilder { GridH2RowDescriptor desc; + List<GridSqlElement[]> elRows = null; + if (stmt instanceof GridSqlInsert) { GridSqlInsert ins = (GridSqlInsert) stmt; target = ins.into(); @@ -136,6 +139,10 @@ public final class UpdatePlanBuilder { cols = ins.columns(); sel = DmlAstUtils.selectForInsertOrMerge(cols, ins.rows(), ins.query()); + + if (sel == null) + elRows = ins.rows(); + isTwoStepSubqry = (ins.query() != null); rowsNum = isTwoStepSubqry ? 0 : ins.rows().size(); } @@ -149,6 +156,10 @@ public final class UpdatePlanBuilder { cols = merge.columns(); sel = DmlAstUtils.selectForInsertOrMerge(cols, merge.rows(), merge.query()); + + if (sel == null) + elRows = merge.rows(); + isTwoStepSubqry = (merge.query() != null); rowsNum = isTwoStepSubqry ? 0 : merge.rows().size(); } @@ -158,7 +169,7 @@ public final class UpdatePlanBuilder { } // Let's set the flag only for subqueries that have their FROM specified. - isTwoStepSubqry = (isTwoStepSubqry && (sel instanceof GridSqlUnion || + isTwoStepSubqry &= (sel != null && (sel instanceof GridSqlUnion || (sel instanceof GridSqlSelect && ((GridSqlSelect) sel).from() != null))); int keyColIdx = -1; @@ -210,13 +221,33 @@ public final class UpdatePlanBuilder { KeyValueSupplier keySupplier = createSupplier(cctx, desc.type(), keyColIdx, hasKeyProps, true, false); KeyValueSupplier valSupplier = createSupplier(cctx, desc.type(), valColIdx, hasValProps, false, false); - String selectSql = sel.getSQL(); + String selectSql = sel != null ? sel.getSQL() : null; DmlDistributedPlanInfo distributed = (rowsNum == 0 && !F.isEmpty(selectSql)) ? checkPlanCanBeDistributed(idx, conn, fieldsQuery, loc, selectSql, tbl.dataTable().cacheName()) : null; UpdateMode mode = stmt instanceof GridSqlMerge ? UpdateMode.MERGE : UpdateMode.INSERT; + List<List<DmlArgument>> rows = null; + + if (elRows != null) { + assert sel == null; + + rows = new ArrayList<>(elRows.size()); + + for (GridSqlElement[] elRow : elRows) { + List<DmlArgument> row = new ArrayList<>(cols.length); + + for (GridSqlElement el : elRow) { + DmlArgument arg = DmlArguments.create(el); + + row.add(arg); + } + + rows.add(row); + } + } + return new UpdatePlan( mode, tbl.dataTable(), @@ -228,6 +259,7 @@ public final class UpdatePlanBuilder { valColIdx, selectSql, !isTwoStepSubqry, + rows, rowsNum, null, distributed @@ -349,6 +381,7 @@ public final class UpdatePlanBuilder { valColIdx, selectSql, false, + null, 0, null, distributed
