Repository: ignite Updated Branches: refs/heads/ignite-6022-proto [created] 771422cb1
IGNITE-4490 Query-less INSERT and MERGE Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/d362da5f Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/d362da5f Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/d362da5f Branch: refs/heads/ignite-6022-proto Commit: d362da5fdb0671d17672b0096d064ce30254638f Parents: c040c37 Author: Alexander Paschenko <[email protected]> Authored: Mon Dec 26 18:39:10 2016 +0300 Committer: Alexander Paschenko <[email protected]> Committed: Mon Dec 26 18:39:10 2016 +0300 ---------------------------------------------------------------------- .../query/h2/DmlStatementsProcessor.java | 91 +++++++++++++++----- .../query/h2/dml/FastUpdateArguments.java | 36 ++++++++ .../processors/query/h2/dml/UpdatePlan.java | 43 +++++---- .../query/h2/dml/UpdatePlanBuilder.java | 59 +++++++++---- .../processors/query/h2/sql/DmlAstUtils.java | 55 +++--------- .../IgniteCacheUpdateSqlQuerySelfTest.java | 2 - 6 files changed, 184 insertions(+), 102 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/d362da5f/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 4030758..f660148 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 @@ -58,6 +58,7 @@ import org.apache.ignite.internal.processors.query.GridQueryFieldsResultAdapter; 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.h2.dml.FastUpdateArgument; import org.apache.ignite.internal.processors.query.h2.dml.FastUpdateArguments; import org.apache.ignite.internal.processors.query.h2.dml.KeyValueSupplier; import org.apache.ignite.internal.processors.query.h2.dml.UpdatePlan; @@ -241,13 +242,15 @@ public class DmlStatementsProcessor { return new UpdateResult(doSingleUpdate(plan, params), X.EMPTY_OBJECT_ARRAY); } - assert !F.isEmpty(plan.selectQry); + assert !F.isEmpty(plan.rows) ^ !F.isEmpty(plan.selectQry); - 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 // subquery and not some dummy stuff like "select 1, 2, 3;" if (!loc && !plan.isLocSubqry) { + assert !F.isEmpty(plan.selectQry); + SqlFieldsQuery newFieldsQry = new SqlFieldsQuery(plan.selectQry, fieldsQry.isCollocated()) .setArgs(params) .setDistributedJoins(fieldsQry.isDistributedJoins()) @@ -256,13 +259,13 @@ public class DmlStatementsProcessor { .setPageSize(fieldsQry.getPageSize()) .setTimeout(fieldsQry.getTimeout(), TimeUnit.MILLISECONDS); - cur = (QueryCursorImpl<List<?>>) indexing.queryTwoStep(cctx, newFieldsQry, cancel); + cur = indexing.queryTwoStep(cctx, newFieldsQry, cancel); } - else { + else if (F.isEmpty(plan.rows)) { final GridQueryFieldsResult res = indexing.queryLocalSqlFields(cctx.name(), plan.selectQry, F.asList(params), filters, fieldsQry.isEnforceJoinOrder(), fieldsQry.getTimeout(), cancel); - cur = new QueryCursorImpl<>(new Iterable<List<?>>() { + QueryCursorImpl<List<?>> resCur = new QueryCursorImpl<>(new Iterable<List<?>>() { @Override public Iterator<List<?>> iterator() { try { return new GridQueryCacheObjectsIterator(res.iterator(), cctx, cctx.keepBinary()); @@ -273,7 +276,34 @@ public class DmlStatementsProcessor { } }, cancel); - cur.fieldsMeta(res.metaData()); + resCur.fieldsMeta(res.metaData()); + + cur = resCur; + } + else { + assert plan.rowsNum > 0 && !F.isEmpty(plan.colNames); + + List<List<?>> args = new ArrayList<>(plan.rowsNum); + + GridH2RowDescriptor desc = plan.tbl.rowDescriptor(); + + for (List<FastUpdateArgument> argRow : plan.rows) { + List<Object> row = new ArrayList<>(); + + for (int j = 0; j < plan.colNames.length; j++) { + Object colVal = argRow.get(j).apply(fieldsQry.getArgs()); + + if (j == plan.keyColIdx || j == plan.valColIdx) + colVal = convert(colVal, j == plan.keyColIdx ? desc.type().keyClass() : desc.type().valueClass(), + desc); + + row.add(colVal); + } + + args.add(row); + } + + cur = args; } int pageSize = loc ? 0 : fieldsQry.getPageSize(); @@ -379,7 +409,7 @@ public class DmlStatementsProcessor { * @return Results of DELETE (number of items affected AND keys that failed to be updated). */ @SuppressWarnings({"unchecked", "ConstantConditions", "ThrowableResultOfMethodCallIgnored"}) - private UpdateResult doDelete(GridCacheContext cctx, QueryCursorImpl<List<?>> cursor, int pageSize) + private UpdateResult doDelete(GridCacheContext cctx, Iterable<List<?>> cursor, int pageSize) throws IgniteCheckedException { // With DELETE, we have only two columns - key and value. long res = 0; @@ -449,7 +479,7 @@ public class DmlStatementsProcessor { * had been modified concurrently (arguments for a re-run)]. */ @SuppressWarnings({"unchecked", "ThrowableResultOfMethodCallIgnored"}) - private UpdateResult doUpdate(UpdatePlan plan, QueryCursorImpl<List<?>> cursor, int pageSize) + private UpdateResult doUpdate(UpdatePlan plan, Iterable<List<?>> cursor, int pageSize) throws IgniteCheckedException { GridH2RowDescriptor desc = plan.tbl.rowDescriptor(); @@ -492,7 +522,7 @@ public class DmlStatementsProcessor { continue; newColVals.put(plan.colNames[i], convert(e.get(i + 2), plan.colNames[i], - plan.tbl.rowDescriptor(), plan.colTypes[i])); + plan.tbl.rowDescriptor())); } newVal = plan.valSupplier.apply(e); @@ -585,12 +615,11 @@ public class DmlStatementsProcessor { * @param val Source value. * @param colName Column name to search for property. * @param desc Row descriptor. - * @param type Expected column type to convert to. * @return Converted object. * @throws IgniteCheckedException if failed. */ @SuppressWarnings({"ConstantConditions", "SuspiciousSystemArraycopy"}) - private static Object convert(Object val, String colName, GridH2RowDescriptor desc, int type) + private static Object convert(Object val, String colName, GridH2RowDescriptor desc) throws IgniteCheckedException { if (val == null) return null; @@ -601,6 +630,21 @@ public class DmlStatementsProcessor { Class<?> expCls = prop.type(); + return convert(val, expCls, desc); + } + + /** + * Convert value to column's expected type by means of H2. + * + * @param val Source value. + * @param expCls Expected property class. + * @param desc Row descriptor. + * @return Converted object. + * @throws IgniteCheckedException if failed. + */ + @SuppressWarnings({"ConstantConditions", "SuspiciousSystemArraycopy"}) + private static Object convert(Object val, Class<?> expCls, GridH2RowDescriptor desc) + throws IgniteCheckedException { Class<?> currCls = val.getClass(); if (val instanceof Date && currCls != Date.class && expCls == Date.class) { @@ -609,6 +653,8 @@ public class DmlStatementsProcessor { return new Date(((Date) val).getTime()); } + int type = DataType.getTypeFromClass(expCls); + // We have to convert arrays of reference types manually - see https://issues.apache.org/jira/browse/IGNITE-4327 // Still, we only can convert from Object[] to something more precise. if (type == Value.ARRAY && currCls != expCls) { @@ -689,14 +735,14 @@ public class DmlStatementsProcessor { * @throws IgniteCheckedException if failed. */ @SuppressWarnings("unchecked") - private long doMerge(UpdatePlan plan, QueryCursorImpl<List<?>> cursor, int pageSize) throws IgniteCheckedException { + private long doMerge(UpdatePlan plan, Iterable<List<?>> cursor, int pageSize) throws IgniteCheckedException { GridH2RowDescriptor desc = plan.tbl.rowDescriptor(); GridCacheContext cctx = desc.context(); // If we have just one item to put, just do so if (plan.rowsNum == 1) { - IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.colTypes, plan.keySupplier, + IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.keySupplier, plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc); cctx.cache().put(t.getKey(), t.getValue()); @@ -709,7 +755,7 @@ public class DmlStatementsProcessor { for (Iterator<List<?>> it = cursor.iterator(); it.hasNext();) { List<?> row = it.next(); - IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.colTypes, plan.keySupplier, plan.valSupplier, + IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.keySupplier, plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc); rows.put(t.getKey(), t.getValue()); @@ -735,14 +781,14 @@ public class DmlStatementsProcessor { * @throws IgniteCheckedException if failed, particularly in case of duplicate keys. */ @SuppressWarnings({"unchecked", "ConstantConditions"}) - private long doInsert(UpdatePlan plan, QueryCursorImpl<List<?>> cursor, int pageSize) throws IgniteCheckedException { + private long doInsert(UpdatePlan plan, Iterable<List<?>> cursor, int pageSize) throws IgniteCheckedException { GridH2RowDescriptor desc = plan.tbl.rowDescriptor(); GridCacheContext cctx = desc.context(); // If we have just one item to put, just do so if (plan.rowsNum == 1) { - IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.colTypes, + IgniteBiTuple t = rowToKeyValue(cctx, cursor.iterator().next().toArray(), plan.colNames, plan.keySupplier, plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc); if (cctx.cache().putIfAbsent(t.getKey(), t.getValue())) @@ -768,7 +814,7 @@ public class DmlStatementsProcessor { while (it.hasNext()) { List<?> row = it.next(); - final IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.colTypes, plan.keySupplier, + final IgniteBiTuple t = rowToKeyValue(cctx, row.toArray(), plan.colNames, plan.keySupplier, plan.valSupplier, plan.keyColIdx, plan.valColIdx, desc); rows.put(t.getKey(), new InsertEntryProcessor(t.getValue())); @@ -838,7 +884,6 @@ public class DmlStatementsProcessor { * @param cctx Cache context. * @param row Row to process. * @param cols Query cols. - * @param colTypes Column types to convert data from {@code row} to. * @param keySupplier Key instantiation method. * @param valSupplier Key instantiation method. * @param keyColIdx Key column index, or {@code -1} if no key column is mentioned in {@code cols}. @@ -848,10 +893,12 @@ public class DmlStatementsProcessor { */ @SuppressWarnings({"unchecked", "ConstantConditions", "ResultOfMethodCallIgnored"}) private IgniteBiTuple<?, ?> rowToKeyValue(GridCacheContext cctx, Object[] row, String[] cols, - int[] colTypes, KeyValueSupplier keySupplier, KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, + KeyValueSupplier keySupplier, KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, GridH2RowDescriptor rowDesc) throws IgniteCheckedException { - Object key = keySupplier.apply(F.asList(row)); - Object val = valSupplier.apply(F.asList(row)); + List<Object> rowList = F.asList(row); + + Object key = keySupplier.apply(rowList); + Object val = valSupplier.apply(rowList); if (key == null) throw new IgniteSQLException("Key for INSERT or MERGE must not be null", IgniteQueryErrorCode.NULL_KEY); @@ -865,7 +912,7 @@ public class DmlStatementsProcessor { if (i == keyColIdx || i == valColIdx) continue; - desc.setValue(cols[i], key, val, convert(row[i], cols[i], rowDesc, colTypes[i])); + desc.setValue(cols[i], key, val, convert(row[i], cols[i], rowDesc)); } if (cctx.binaryMarshaller()) { http://git-wip-us.apache.org/repos/asf/ignite/blob/d362da5f/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArguments.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArguments.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArguments.java index cb47704..056dfaa 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArguments.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/dml/FastUpdateArguments.java @@ -50,4 +50,40 @@ public final class FastUpdateArguments { return null; } }; + + /** Simple constant value based operand. */ + public final static class ValueArgument implements FastUpdateArgument { + /** Value to return. */ + private final Object val; + + /** */ + public ValueArgument(Object val) { + this.val = val; + } + + /** {@inheritDoc} */ + @Override public Object apply(Object[] arg) throws IgniteCheckedException { + return val; + } + } + + /** Simple constant value based operand. */ + public final static class ParamArgument implements FastUpdateArgument { + /** Value to return. */ + private final int paramIdx; + + /** */ + public 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/d362da5f/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 b81ac60..9bd1ecf 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,7 @@ package org.apache.ignite.internal.processors.query.h2.dml; +import java.util.List; import org.apache.ignite.internal.processors.query.h2.opt.GridH2Table; import org.apache.ignite.internal.util.typedef.F; @@ -33,12 +34,6 @@ public final class UpdatePlan { /** Column names to set or update. */ public final String[] colNames; - /** - * Expected column types to set or insert/merge. - * @see org.h2.value.Value - */ - public final int[] colTypes; - /** Method to create key for INSERT or MERGE, ignored for UPDATE and DELETE. */ public final KeyValueSupplier keySupplier; @@ -58,6 +53,9 @@ public final class UpdatePlan { /** Subquery flag - {@code true} if {@link #selectQry} is an actual subquery that retrieves data from some cache. */ public final boolean isLocSubqry; + /** */ + public final Iterable<List<FastUpdateArgument>> rows; + /** Number of rows in rows based MERGE or INSERT. */ public final int rowsNum; @@ -65,11 +63,11 @@ public final class UpdatePlan { public final FastUpdateArguments fastUpdateArgs; /** */ - private UpdatePlan(UpdateMode mode, GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier keySupplier, + private UpdatePlan(UpdateMode mode, GridH2Table tbl, String[] colNames, KeyValueSupplier keySupplier, KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, String selectQry, boolean isLocSubqry, - int rowsNum, FastUpdateArguments fastUpdateArgs) { + Iterable<List<FastUpdateArgument>> rows, int rowsNum, FastUpdateArguments fastUpdateArgs) { this.colNames = colNames; - this.colTypes = colTypes; + this.rows = rows; this.rowsNum = rowsNum; assert mode != null; assert tbl != null; @@ -86,43 +84,44 @@ public final class UpdatePlan { } /** */ - public static UpdatePlan forMerge(GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier keySupplier, + public static UpdatePlan forMerge(GridH2Table tbl, String[] colNames, KeyValueSupplier keySupplier, KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, String selectQry, boolean isLocSubqry, - int rowsNum) { + Iterable<List<FastUpdateArgument>> rows, int rowsNum) { assert !F.isEmpty(colNames); - return new UpdatePlan(UpdateMode.MERGE, tbl, colNames, colTypes, keySupplier, valSupplier, keyColIdx, valColIdx, - selectQry, isLocSubqry, rowsNum, null); + return new UpdatePlan(UpdateMode.MERGE, tbl, colNames, keySupplier, valSupplier, keyColIdx, valColIdx, + selectQry, isLocSubqry, rows, rowsNum, null); } /** */ - public static UpdatePlan forInsert(GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier keySupplier, - KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, String selectQry, boolean isLocSubqry, int rowsNum) { + public static UpdatePlan forInsert(GridH2Table tbl, String[] colNames, KeyValueSupplier keySupplier, + KeyValueSupplier valSupplier, int keyColIdx, int valColIdx, String selectQry, boolean isLocSubqry, + Iterable<List<FastUpdateArgument>> rows, int rowsNum) { assert !F.isEmpty(colNames); - return new UpdatePlan(UpdateMode.INSERT, tbl, colNames, colTypes, keySupplier, valSupplier, keyColIdx, valColIdx, - selectQry, isLocSubqry, rowsNum, null); + return new UpdatePlan(UpdateMode.INSERT, tbl, colNames, keySupplier, valSupplier, keyColIdx, valColIdx, + selectQry, isLocSubqry, rows, rowsNum, null); } /** */ - public static UpdatePlan forUpdate(GridH2Table tbl, String[] colNames, int[] colTypes, KeyValueSupplier valSupplier, + public static UpdatePlan forUpdate(GridH2Table tbl, String[] colNames, KeyValueSupplier valSupplier, int valColIdx, String selectQry) { assert !F.isEmpty(colNames); - return new UpdatePlan(UpdateMode.UPDATE, tbl, colNames, colTypes, null, valSupplier, -1, valColIdx, selectQry, - false, 0, null); + return new UpdatePlan(UpdateMode.UPDATE, tbl, colNames, null, valSupplier, -1, valColIdx, selectQry, + false, null, 0, null); } /** */ public static UpdatePlan forDelete(GridH2Table tbl, String selectQry) { - return new UpdatePlan(UpdateMode.DELETE, tbl, null, null, null, null, -1, -1, selectQry, false, 0, null); + return new UpdatePlan(UpdateMode.DELETE, tbl, null, null, null, -1, -1, selectQry, false, null, 0, null); } /** */ public static UpdatePlan forFastUpdate(UpdateMode mode, GridH2Table tbl, FastUpdateArguments fastUpdateArgs) { assert mode == UpdateMode.UPDATE || mode == UpdateMode.DELETE; - return new UpdatePlan(mode, tbl, null, null, null, null, -1, -1, null, false, 0, fastUpdateArgs); + return new UpdatePlan(mode, tbl, null, null, null, -1, -1, null, false, null, 0, fastUpdateArgs); } } http://git-wip-us.apache.org/repos/asf/ignite/blob/d362da5f/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 fdcd164..0303fa4 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 @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.query.h2.dml; import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -36,10 +37,12 @@ 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.processors.query.h2.sql.DmlAstUtils; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlColumn; +import org.apache.ignite.internal.processors.query.h2.sql.GridSqlConst; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlDelete; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlElement; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlInsert; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlMerge; +import org.apache.ignite.internal.processors.query.h2.sql.GridSqlParameter; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQuery; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlQueryParser; import org.apache.ignite.internal.processors.query.h2.sql.GridSqlSelect; @@ -107,6 +110,10 @@ public final class UpdatePlanBuilder { GridH2RowDescriptor desc; + List<GridSqlElement[]> elRows = null; + + List<List<FastUpdateArgument>> rows = null; + if (stmt instanceof GridSqlInsert) { GridSqlInsert ins = (GridSqlInsert) stmt; target = ins.into(); @@ -116,6 +123,10 @@ public final class UpdatePlanBuilder { cols = ins.columns(); sel = DmlAstUtils.selectForInsertOrMerge(cols, ins.rows(), ins.query(), desc); + + if (sel == null) + elRows = ins.rows(); + isTwoStepSubqry = (ins.query() != null); rowsNum = isTwoStepSubqry ? 0 : ins.rows().size(); } @@ -137,14 +148,40 @@ public final class UpdatePlanBuilder { cols = merge.columns(); sel = DmlAstUtils.selectForInsertOrMerge(cols, merge.rows(), merge.query(), desc); + + if (sel == null) + elRows = merge.rows(); + isTwoStepSubqry = (merge.query() != null); rowsNum = isTwoStepSubqry ? 0 : merge.rows().size(); } else throw new IgniteSQLException("Unexpected DML operation [cls=" + stmt.getClass().getName() + ']', IgniteQueryErrorCode.UNEXPECTED_OPERATION); + if (elRows != null) { + assert sel == null; + + rows = new ArrayList<>(elRows.size()); + + for (GridSqlElement[] elRow : elRows) { + List<FastUpdateArgument> row = new ArrayList<>(cols.length); + + for (GridSqlElement e : elRow) { + if (e instanceof GridSqlConst) + row.add(new FastUpdateArguments.ValueArgument(((GridSqlConst) e).value().getObject())); + else if (e instanceof GridSqlParameter) + row.add(new FastUpdateArguments.ParamArgument(((GridSqlParameter) e).index())); + else + throw new IgniteSQLException("Unexpected element type: " + e.getClass().getSimpleName(), + IgniteQueryErrorCode.UNEXPECTED_ELEMENT_TYPE); + } + + rows.add(row); + } + } + // 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; @@ -161,8 +198,6 @@ public final class UpdatePlanBuilder { String[] colNames = new String[cols.length]; - int[] colTypes = new int[cols.length]; - for (int i = 0; i < cols.length; i++) { GridSqlColumn col = cols[i]; @@ -170,8 +205,6 @@ public final class UpdatePlanBuilder { colNames[i] = colName; - colTypes[i] = col.resultType().type(); - if (KEY_FIELD_NAME.equals(colName)) { keyColIdx = i; continue; @@ -196,11 +229,11 @@ public final class UpdatePlanBuilder { KeyValueSupplier valSupplier = createSupplier(cctx, desc.type(), valColIdx, hasValProps, false); if (stmt instanceof GridSqlMerge) - return UpdatePlan.forMerge(tbl.dataTable(), colNames, colTypes, keySupplier, valSupplier, keyColIdx, - valColIdx, sel.getSQL(), !isTwoStepSubqry, rowsNum); + return UpdatePlan.forMerge(tbl.dataTable(), colNames, keySupplier, valSupplier, keyColIdx, + valColIdx, sel != null ? sel.getSQL() : null, !isTwoStepSubqry, rows, rowsNum); else - return UpdatePlan.forInsert(tbl.dataTable(), colNames, colTypes, keySupplier, valSupplier, keyColIdx, - valColIdx, sel.getSQL(), !isTwoStepSubqry, rowsNum); + return UpdatePlan.forInsert(tbl.dataTable(), colNames, keySupplier, valSupplier, keyColIdx, + valColIdx, sel != null ? sel.getSQL() : null, !isTwoStepSubqry, rows, rowsNum); } /** @@ -261,13 +294,9 @@ public final class UpdatePlanBuilder { String[] colNames = new String[updatedCols.size()]; - int[] colTypes = new int[updatedCols.size()]; - for (int i = 0; i < updatedCols.size(); i++) { colNames[i] = updatedCols.get(i).columnName(); - colTypes[i] = updatedCols.get(i).resultType().type(); - if (VAL_FIELD_NAME.equals(colNames[i])) valColIdx = i; } @@ -299,7 +328,7 @@ public final class UpdatePlanBuilder { sel = DmlAstUtils.selectForUpdate((GridSqlUpdate) stmt, errKeysPos); - return UpdatePlan.forUpdate(gridTbl, colNames, colTypes, newValSupplier, valColIdx, sel.getSQL()); + return UpdatePlan.forUpdate(gridTbl, colNames, newValSupplier, valColIdx, sel.getSQL()); } else { sel = DmlAstUtils.selectForDelete((GridSqlDelete) stmt, errKeysPos); @@ -323,7 +352,7 @@ public final class UpdatePlanBuilder { */ @SuppressWarnings({"ConstantConditions", "unchecked"}) private static KeyValueSupplier createSupplier(final GridCacheContext<?, ?> cctx, GridQueryTypeDescriptor desc, - final int colIdx, boolean hasProps, final boolean key) throws IgniteCheckedException { + final int colIdx, boolean hasProps, final boolean key) throws IgniteCheckedException { final String typeName = key ? desc.keyTypeName() : desc.valueTypeName(); //Try to find class for the key locally. http://git-wip-us.apache.org/repos/asf/ignite/blob/d362da5f/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java index 6deb146..8df786c 100644 --- a/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java +++ b/modules/indexing/src/main/java/org/apache/ignite/internal/processors/query/h2/sql/DmlAstUtils.java @@ -21,7 +21,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.apache.ignite.IgniteCheckedException; import org.apache.ignite.IgniteException; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing; @@ -67,7 +66,7 @@ public final class DmlAstUtils { * @param rows Rows to create pseudo-SELECT upon. * @param subQry Subquery to use rather than rows. * @param desc Row descriptor. - * @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, GridH2RowDescriptor desc) { @@ -82,6 +81,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()); @@ -105,10 +106,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 { @@ -202,9 +211,9 @@ public final class DmlAstUtils { return FastUpdateArguments.NULL_ARGUMENT; if (el instanceof GridSqlConst) - return new ValueArgument(((GridSqlConst)el).value().getObject()); + return new FastUpdateArguments.ValueArgument(((GridSqlConst)el).value().getObject()); else - return new ParamArgument(((GridSqlParameter)el).index()); + return new FastUpdateArguments.ParamArgument(((GridSqlParameter)el).index()); } /** @@ -577,40 +586,4 @@ public final class DmlAstUtils { } }); } - - /** Simple constant value based operand. */ - private final static class ValueArgument implements FastUpdateArgument { - /** Value to return. */ - private final Object val; - - /** */ - private ValueArgument(Object val) { - this.val = val; - } - - /** {@inheritDoc} */ - @Override public Object apply(Object[] arg) throws IgniteCheckedException { - return val; - } - } - - /** Simple constant value based operand. */ - private final static class ParamArgument implements FastUpdateArgument { - /** Value to return. */ - private final int paramIdx; - - /** */ - 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/d362da5f/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java ---------------------------------------------------------------------- diff --git a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java index 332a082..5ee21b2 100644 --- a/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java +++ b/modules/indexing/src/test/java/org/apache/ignite/internal/processors/cache/IgniteCacheUpdateSqlQuerySelfTest.java @@ -182,8 +182,6 @@ public class IgniteCacheUpdateSqlQuerySelfTest extends IgniteCacheAbstractSqlDml cache.query(new SqlFieldsQuery("insert into \"AllTypes\"(_key, _val, \"dateCol\", \"booleanCol\"," + "\"tsCol\") values(2, ?, '2016-11-30 12:00:00', false, DATE '2016-12-01')").setArgs(new AllTypes(2L))); - List<?> ll = cache.query(new SqlFieldsQuery("select \"primitiveIntsCol\" from \"AllTypes\"")).getAll(); - cache.query(new SqlFieldsQuery("update \"AllTypes\" set \"doubleCol\" = CAST('50' as INT)," + " \"booleanCol\" = 80, \"innerTypeCol\" = ?, \"strCol\" = PI(), \"shortCol\" = " + "CAST(WEEK(PARSEDATETIME('2016-11-30', 'yyyy-MM-dd')) as VARCHAR), " +
