From 89ae7b781ec482b251049367e8e57c51cf86d356 Mon Sep 17 00:00:00 2001 From: agrgr Date: Mon, 1 Dec 2025 22:49:14 +0100 Subject: [PATCH 1/6] decouple from Aerospike Java client --- pom.xml | 23 +- src/main/java/com/aerospike/dsl/Index.java | 6 +- .../java/com/aerospike/dsl/IndexContext.java | 7 +- .../java/com/aerospike/dsl/ParseResult.java | 4 +- .../com/aerospike/dsl/ParsedExpression.java | 4 +- .../java/com/aerospike/dsl/api/DSLParser.java | 4 +- .../dsl/client/AerospikeException.java | 170 ++ .../com/aerospike/dsl/client/ResultCode.java | 802 ++++++++ .../java/com/aerospike/dsl/client/Value.java | 1647 +++++++++++++++++ .../com/aerospike/dsl/client/cdt/CTX.java | 168 ++ .../dsl/client/cdt/ListReturnType.java | 88 + .../aerospike/dsl/client/cdt/MapOrder.java | 48 + .../dsl/client/cdt/MapReturnType.java | 113 ++ .../aerospike/dsl/client/command/Buffer.java | 643 +++++++ .../aerospike/dsl/client/command/Command.java | 109 ++ .../dsl/client/command/ParticleType.java | 34 + .../com/aerospike/dsl/client/exp/Exp.java | 1555 ++++++++++++++++ .../dsl/client/exp/ExpReadFlags.java | 32 + .../dsl/client/exp/ExpWriteFlags.java | 65 + .../aerospike/dsl/client/exp/Expression.java | 121 ++ .../com/aerospike/dsl/client/exp/ListExp.java | 328 ++++ .../com/aerospike/dsl/client/exp/MapExp.java | 378 ++++ .../aerospike/dsl/client/query/Filter.java | 799 ++++++++ .../dsl/client/query/IndexCollectionType.java | 42 + .../aerospike/dsl/client/query/IndexType.java | 42 + .../aerospike/dsl/client/query/RegexFlag.java | 51 + .../com/aerospike/dsl/client/util/Crypto.java | 36 + .../com/aerospike/dsl/client/util/Pack.java | 380 ++++ .../com/aerospike/dsl/client/util/Packer.java | 607 ++++++ .../dsl/client/util/ThreadLocalData.java | 71 + .../aerospike/dsl/client/util/Unpacker.java | 518 ++++++ .../com/aerospike/dsl/client/util/Utf8.java | 95 + .../com/aerospike/dsl/impl/DSLParserImpl.java | 2 +- .../com/aerospike/dsl/parts/AbstractPart.java | 6 +- .../com/aerospike/dsl/parts/cdt/CdtPart.java | 4 +- .../dsl/parts/cdt/list/ListIndex.java | 6 +- .../dsl/parts/cdt/list/ListIndexRange.java | 8 +- .../dsl/parts/cdt/list/ListPart.java | 4 +- .../dsl/parts/cdt/list/ListRank.java | 6 +- .../dsl/parts/cdt/list/ListRankRange.java | 8 +- .../parts/cdt/list/ListRankRangeRelative.java | 10 +- .../parts/cdt/list/ListTypeDesignator.java | 6 +- .../dsl/parts/cdt/list/ListValue.java | 8 +- .../dsl/parts/cdt/list/ListValueList.java | 8 +- .../dsl/parts/cdt/list/ListValueRange.java | 8 +- .../aerospike/dsl/parts/cdt/map/MapIndex.java | 6 +- .../dsl/parts/cdt/map/MapIndexRange.java | 8 +- .../parts/cdt/map/MapIndexRangeRelative.java | 10 +- .../aerospike/dsl/parts/cdt/map/MapKey.java | 8 +- .../dsl/parts/cdt/map/MapKeyList.java | 8 +- .../dsl/parts/cdt/map/MapKeyRange.java | 8 +- .../aerospike/dsl/parts/cdt/map/MapPart.java | 4 +- .../aerospike/dsl/parts/cdt/map/MapRank.java | 6 +- .../dsl/parts/cdt/map/MapRankRange.java | 8 +- .../parts/cdt/map/MapRankRangeRelative.java | 10 +- .../dsl/parts/cdt/map/MapTypeDesignator.java | 6 +- .../aerospike/dsl/parts/cdt/map/MapValue.java | 8 +- .../dsl/parts/cdt/map/MapValueList.java | 8 +- .../dsl/parts/cdt/map/MapValueRange.java | 8 +- .../dsl/parts/operand/BooleanOperand.java | 2 +- .../dsl/parts/operand/FloatOperand.java | 2 +- .../dsl/parts/operand/IntOperand.java | 2 +- .../dsl/parts/operand/ListOperand.java | 2 +- .../dsl/parts/operand/MapOperand.java | 2 +- .../dsl/parts/operand/MetadataOperand.java | 6 +- .../dsl/parts/operand/StringOperand.java | 2 +- .../dsl/parts/operand/VariableOperand.java | 2 +- .../aerospike/dsl/parts/path/BasePath.java | 2 +- .../com/aerospike/dsl/parts/path/BinPart.java | 2 +- .../com/aerospike/dsl/parts/path/Path.java | 4 +- .../dsl/parts/path/PathFunction.java | 2 +- .../aerospike/dsl/util/PathOperandUtils.java | 8 +- .../com/aerospike/dsl/util/TypeUtils.java | 2 +- .../aerospike/dsl/util/ValidationUtils.java | 2 +- .../visitor/ExpressionConditionVisitor.java | 2 +- .../visitor/NoApplicableFilterException.java | 2 +- .../aerospike/dsl/visitor/VisitorUtils.java | 8 +- .../java/com/aerospike/dsl/ctx/CtxTests.java | 4 +- .../ArithmeticExpressionsTests.java | 2 +- .../dsl/expression/BinExpressionsTests.java | 2 +- .../dsl/expression/CastingTests.java | 2 +- .../expression/ControlStructuresTests.java | 2 +- .../dsl/expression/ExplicitTypesTests.java | 2 +- .../dsl/expression/ImplicitTypesTests.java | 2 +- .../dsl/expression/ListExpressionsTests.java | 8 +- .../expression/LogicalExpressionsTests.java | 2 +- .../MapAndListExpressionsTests.java | 14 +- .../dsl/expression/MapExpressionsTests.java | 14 +- .../dsl/expression/RecordMetadataTests.java | 10 +- .../dsl/filter/ArithmeticFiltersTests.java | 4 +- .../aerospike/dsl/filter/BinFiltersTests.java | 4 +- .../dsl/filter/ExplicitTypesFiltersTests.java | 4 +- .../dsl/filter/ListExpressionsTests.java | 10 +- .../LogicalParsedExpressionTests.java | 6 +- .../parsedExpression/PlaceholdersTests.java | 20 +- .../com/aerospike/dsl/util/TestUtils.java | 8 +- 96 files changed, 9152 insertions(+), 202 deletions(-) create mode 100644 src/main/java/com/aerospike/dsl/client/AerospikeException.java create mode 100644 src/main/java/com/aerospike/dsl/client/ResultCode.java create mode 100644 src/main/java/com/aerospike/dsl/client/Value.java create mode 100644 src/main/java/com/aerospike/dsl/client/cdt/CTX.java create mode 100644 src/main/java/com/aerospike/dsl/client/cdt/ListReturnType.java create mode 100644 src/main/java/com/aerospike/dsl/client/cdt/MapOrder.java create mode 100644 src/main/java/com/aerospike/dsl/client/cdt/MapReturnType.java create mode 100644 src/main/java/com/aerospike/dsl/client/command/Buffer.java create mode 100644 src/main/java/com/aerospike/dsl/client/command/Command.java create mode 100644 src/main/java/com/aerospike/dsl/client/command/ParticleType.java create mode 100644 src/main/java/com/aerospike/dsl/client/exp/Exp.java create mode 100644 src/main/java/com/aerospike/dsl/client/exp/ExpReadFlags.java create mode 100644 src/main/java/com/aerospike/dsl/client/exp/ExpWriteFlags.java create mode 100644 src/main/java/com/aerospike/dsl/client/exp/Expression.java create mode 100644 src/main/java/com/aerospike/dsl/client/exp/ListExp.java create mode 100644 src/main/java/com/aerospike/dsl/client/exp/MapExp.java create mode 100644 src/main/java/com/aerospike/dsl/client/query/Filter.java create mode 100644 src/main/java/com/aerospike/dsl/client/query/IndexCollectionType.java create mode 100644 src/main/java/com/aerospike/dsl/client/query/IndexType.java create mode 100644 src/main/java/com/aerospike/dsl/client/query/RegexFlag.java create mode 100644 src/main/java/com/aerospike/dsl/client/util/Crypto.java create mode 100644 src/main/java/com/aerospike/dsl/client/util/Pack.java create mode 100644 src/main/java/com/aerospike/dsl/client/util/Packer.java create mode 100644 src/main/java/com/aerospike/dsl/client/util/ThreadLocalData.java create mode 100644 src/main/java/com/aerospike/dsl/client/util/Unpacker.java create mode 100644 src/main/java/com/aerospike/dsl/client/util/Utf8.java diff --git a/pom.xml b/pom.xml index c26f1ac..bb2c0c9 100644 --- a/pom.xml +++ b/pom.xml @@ -21,12 +21,13 @@ 3.5.0 3.3.1 1.6 - 9.2.0 4.13.2 1.18.42 5.14.0 3.27.6 0.8.0 + 2.0.1 + 1.5.21 @@ -62,11 +63,11 @@ - - com.aerospike - aerospike-client-jdk8 - ${aerospike-client-jdk8.version} - + + + + + org.antlr antlr4-runtime @@ -77,6 +78,16 @@ lombok ${lombok.version} + + org.gnu + gnu-crypto + ${gnu.crypto.version} + + + ch.qos.logback + logback-classic + ${logback.version} + org.junit.jupiter junit-jupiter diff --git a/src/main/java/com/aerospike/dsl/Index.java b/src/main/java/com/aerospike/dsl/Index.java index b408e70..6c68f84 100644 --- a/src/main/java/com/aerospike/dsl/Index.java +++ b/src/main/java/com/aerospike/dsl/Index.java @@ -1,8 +1,8 @@ package com.aerospike.dsl; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.query.IndexCollectionType; -import com.aerospike.client.query.IndexType; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.query.IndexCollectionType; +import com.aerospike.dsl.client.query.IndexType; import lombok.Builder; import lombok.EqualsAndHashCode; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/IndexContext.java b/src/main/java/com/aerospike/dsl/IndexContext.java index 17abff6..d655963 100644 --- a/src/main/java/com/aerospike/dsl/IndexContext.java +++ b/src/main/java/com/aerospike/dsl/IndexContext.java @@ -1,24 +1,23 @@ package com.aerospike.dsl; -import com.aerospike.client.query.Filter; import lombok.AllArgsConstructor; import lombok.Getter; import java.util.Collection; /** - * This class stores namespace and indexes required to build secondary index {@link Filter} + * This class stores namespace and indexes required to build secondary index Filter */ @AllArgsConstructor(staticName = "of") @Getter public class IndexContext { /** - * Namespace to be used for creating {@link Filter}. Is matched with namespace of indexes + * Namespace to be used for creating secondary index Filter. Is matched with namespace of indexes */ private String namespace; /** - * Collection of {@link Index} objects to be used for creating {@link Filter}. + * Collection of {@link Index} objects to be used for creating secondary index Filter. * Namespace of indexes is matched with the given {@link #namespace}, bin name and index type are matched * with bins in DSL String */ diff --git a/src/main/java/com/aerospike/dsl/ParseResult.java b/src/main/java/com/aerospike/dsl/ParseResult.java index d429498..c5985d9 100644 --- a/src/main/java/com/aerospike/dsl/ParseResult.java +++ b/src/main/java/com/aerospike/dsl/ParseResult.java @@ -1,7 +1,7 @@ package com.aerospike.dsl; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.query.Filter; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.query.Filter; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/ParsedExpression.java b/src/main/java/com/aerospike/dsl/ParsedExpression.java index 98ef7d6..e7778eb 100644 --- a/src/main/java/com/aerospike/dsl/ParsedExpression.java +++ b/src/main/java/com/aerospike/dsl/ParsedExpression.java @@ -1,8 +1,8 @@ package com.aerospike.dsl; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.query.Filter; import com.aerospike.dsl.annotation.Beta; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.query.Filter; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.parts.ExpressionContainer; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/api/DSLParser.java b/src/main/java/com/aerospike/dsl/api/DSLParser.java index 80d38b2..370bf1d 100644 --- a/src/main/java/com/aerospike/dsl/api/DSLParser.java +++ b/src/main/java/com/aerospike/dsl/api/DSLParser.java @@ -1,12 +1,12 @@ package com.aerospike.dsl.api; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.query.Filter; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.Index; import com.aerospike.dsl.IndexContext; import com.aerospike.dsl.ParsedExpression; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.query.Filter; /** * Contains API to convert dot separated String path into an Aerospike filter - diff --git a/src/main/java/com/aerospike/dsl/client/AerospikeException.java b/src/main/java/com/aerospike/dsl/client/AerospikeException.java new file mode 100644 index 0000000..fc8f6c6 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/AerospikeException.java @@ -0,0 +1,170 @@ +/* + * Copyright 2012-2025 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client; + +import java.util.List; + +/** + * Aerospike exceptions that can be thrown from the client. + */ +public class AerospikeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + protected List subExceptions; + protected int resultCode = ResultCode.CLIENT_ERROR; + protected int iteration = -1; + protected boolean inDoubt; + + public AerospikeException(int resultCode, String message) { + super(message); + this.resultCode = resultCode; + } + + public AerospikeException(int resultCode, Throwable e) { + super(e); + this.resultCode = resultCode; + } + + public AerospikeException(int resultCode) { + super(); + this.resultCode = resultCode; + } + + public AerospikeException(int resultCode, boolean inDoubt) { + super(); + this.resultCode = resultCode; + this.inDoubt = inDoubt; + } + + public AerospikeException(int resultCode, String message, Throwable e) { + super(message, e); + this.resultCode = resultCode; + } + + public AerospikeException(String message, Throwable e) { + super(message, e); + } + + public AerospikeException(String message) { + super(message); + } + + public AerospikeException(Throwable e) { + super(e); + } + + /** + * Exception thrown when a Java serialization error occurs. + */ + public static final class Serialize extends AerospikeException { + private static final long serialVersionUID = 1L; + + public Serialize(Throwable e) { + super(ResultCode.SERIALIZE_ERROR, "Serialize error", e); + } + + public Serialize(String message) { + super(ResultCode.SERIALIZE_ERROR, message); + } + } + + /** + * Exception thrown when client can't parse data returned from server. + */ + public static final class Parse extends AerospikeException { + private static final long serialVersionUID = 1L; + + public Parse(String message) { + super(ResultCode.PARSE_ERROR, message); + } + } + + /** + * Return base message without extra metadata. + */ + public String getBaseMessage() { + String message = super.getMessage(); + return (message != null)? message : ResultCode.getResultString(resultCode); + } + + /** + * Should connection be put back into pool. + */ + public final boolean keepConnection() { + return ResultCode.keepConnection(resultCode); + } + + /** + * Get sub exceptions. Will be null if a retry did not occur. + */ + public final List getSubExceptions() { + return subExceptions; + } + + /** + * Set sub exceptions. + */ + public final void setSubExceptions(List subExceptions) { + this.subExceptions = subExceptions; + } + + /** + * Get integer result code. + */ + public final int getResultCode() { + return resultCode; + } + + /** + * Get number of attempts before failing. + */ + public final int getIteration() { + return iteration; + } + + /** + * Set number of attempts before failing. + */ + public final void setIteration(int iteration) { + this.iteration = iteration; + } + + /** + * Is it possible that write command may have completed. + */ + public final boolean getInDoubt() { + return inDoubt; + } + + /** + * Set whether it is possible that the write command may have completed + * even though this exception was generated. This may be the case when a + * client error occurs (like timeout) after the command was sent to the server. + */ + public final void setInDoubt(boolean isWrite, int commandSentCounter) { + if (isWrite && (commandSentCounter > 1 || (commandSentCounter == 1 && (resultCode == ResultCode.TIMEOUT || resultCode <= 0)))) { + this.inDoubt = true; + } + } + + /** + * Sets the inDoubt value to inDoubt. + */ + public void setInDoubt(boolean inDoubt) { + this.inDoubt = inDoubt; + } +} diff --git a/src/main/java/com/aerospike/dsl/client/ResultCode.java b/src/main/java/com/aerospike/dsl/client/ResultCode.java new file mode 100644 index 0000000..716dd56 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/ResultCode.java @@ -0,0 +1,802 @@ +/* + * Copyright 2012-2025 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client; + +/** + * Database operation error codes. The positive numbers align with the server + * side file proto.h. + */ +public final class ResultCode { + /** + * Transaction commit called, but the transaction was already aborted. + */ + public static final int TXN_ALREADY_ABORTED = -19; + + /** + * Transaction abort called, but the transaction was already committed. + */ + public static final int TXN_ALREADY_COMMITTED = -18; + + /** + * Transaction failed. + */ + public static final int TXN_FAILED = -17; + + /** + * One or more keys failed in a batch. + */ + public static final int BATCH_FAILED = -16; + + /** + * No response received from server. + */ + public static final int NO_RESPONSE = -15; + + /** + * Max errors limit reached. + */ + public static final int MAX_ERROR_RATE = -12; + + /** + * Max retries limit reached. + */ + public static final int MAX_RETRIES_EXCEEDED = -11; + + /** + * Client serialization error. + */ + public static final int SERIALIZE_ERROR = -10; + + /** + * Async delay queue is full. + */ + public static final int ASYNC_QUEUE_FULL = -9; + + /** + * Connection to server failed. + */ + public static final int SERVER_NOT_AVAILABLE = -8; + + /** + * Max connections would be exceeded. There are no more available connections. + */ + public static final int NO_MORE_CONNECTIONS = -7; + + /** + * Query was terminated by user. + */ + public static final int QUERY_TERMINATED = -5; + + /** + * Scan was terminated by user. + */ + public static final int SCAN_TERMINATED = -4; + + /** + * Chosen node is not currently active. + */ + public static final int INVALID_NODE_ERROR = -3; + + /** + * Client parse error. + */ + public static final int PARSE_ERROR = -2; + + /** + * Generic client error. + */ + public static final int CLIENT_ERROR = -1; + + /** + * Operation was successful. + */ + public static final int OK = 0; + + /** + * Unknown server failure. + */ + public static final int SERVER_ERROR = 1; + + /** + * On retrieving, touching or replacing a record that doesn't exist. + */ + public static final int KEY_NOT_FOUND_ERROR = 2; + + /** + * On modifying a record with unexpected generation. + */ + public static final int GENERATION_ERROR = 3; + + /** + * Bad parameter(s) were passed in database operation call. + */ + public static final int PARAMETER_ERROR = 4; + + /** + * On create-only (write unique) operations on a record that already + * exists. + */ + public static final int KEY_EXISTS_ERROR = 5; + + /** + * Bin already exists on a create-only operation. + */ + public static final int BIN_EXISTS_ERROR = 6; + + /** + * Expected cluster was not received. + */ + public static final int CLUSTER_KEY_MISMATCH = 7; + + /** + * Server has run out of memory. + */ + public static final int SERVER_MEM_ERROR = 8; + + /** + * Client or server has timed out. + */ + public static final int TIMEOUT = 9; + + /** + * Operation not allowed in current configuration. + */ + public static final int ALWAYS_FORBIDDEN = 10; + + /** + * Partition is unavailable. + */ + public static final int PARTITION_UNAVAILABLE = 11; + + /** + * Operation is not supported with configured bin type. + */ + public static final int BIN_TYPE_ERROR = 12; + + /** + * Record size exceeds limit. + */ + public static final int RECORD_TOO_BIG = 13; + + /** + * Too many concurrent operations on the same record. + */ + public static final int KEY_BUSY = 14; + + /** + * Scan aborted by server. + */ + public static final int SCAN_ABORT = 15; + + /** + * Unsupported Server Feature (e.g. Scan + UDF) + */ + public static final int UNSUPPORTED_FEATURE = 16; + + /** + * Bin not found on update-only operation. + */ + public static final int BIN_NOT_FOUND = 17; + + /** + * Device not keeping up with writes. + */ + public static final int DEVICE_OVERLOAD = 18; + + /** + * Key type mismatch. + */ + public static final int KEY_MISMATCH = 19; + + /** + * Invalid namespace. + */ + public static final int INVALID_NAMESPACE = 20; + + /** + * Bin name length greater than 15 characters or maximum bins exceeded. + */ + public static final int BIN_NAME_TOO_LONG = 21; + + /** + * Operation not allowed at this time. + */ + public static final int FAIL_FORBIDDEN = 22; + + /** + * Map element not found in UPDATE_ONLY write mode. + */ + public static final int ELEMENT_NOT_FOUND = 23; + + /** + * Map element exists in CREATE_ONLY write mode. + */ + public static final int ELEMENT_EXISTS = 24; + + /** + * Attempt to use an Enterprise feature on a Community server or a server + * without the applicable feature key. + */ + public static final int ENTERPRISE_ONLY = 25; + + /** + * The operation cannot be applied to the current bin value on the server. + */ + public static final int OP_NOT_APPLICABLE = 26; + + /** + * The command was not performed because the filter was false. + */ + public static final int FILTERED_OUT = 27; + + /** + * Write command loses conflict to XDR. + */ + public static final int LOST_CONFLICT = 28; + + /** + * Write can't complete until XDR finishes shipping. + */ + public static final int XDR_KEY_BUSY = 32; + + /** + * There are no more records left for query. + */ + public static final int QUERY_END = 50; + + /** + * Security functionality not supported by connected server. + */ + public static final int SECURITY_NOT_SUPPORTED = 51; + + /** + * Security functionality not enabled by connected server. + */ + public static final int SECURITY_NOT_ENABLED = 52; + + /** + * Security type not supported by connected server. + */ + public static final int SECURITY_SCHEME_NOT_SUPPORTED = 53; + + /** + * Administration command is invalid. + */ + public static final int INVALID_COMMAND = 54; + + /** + * Administration field is invalid. + */ + public static final int INVALID_FIELD = 55; + + /** + * Security protocol not followed. + */ + public static final int ILLEGAL_STATE = 56; + + /** + * User name is invalid. + */ + public static final int INVALID_USER = 60; + + /** + * User was previously created. + */ + public static final int USER_ALREADY_EXISTS = 61; + + /** + * Password is invalid. + */ + public static final int INVALID_PASSWORD = 62; + + /** + * Password has expired. + */ + public static final int EXPIRED_PASSWORD = 63; + + /** + * Forbidden password (e.g. recently used) + */ + public static final int FORBIDDEN_PASSWORD = 64; + + /** + * Security credential is invalid. + */ + public static final int INVALID_CREDENTIAL = 65; + + /** + * Login session expired. + */ + public static final int EXPIRED_SESSION = 66; + + /** + * Role name is invalid. + */ + public static final int INVALID_ROLE = 70; + + /** + * Role already exists. + */ + public static final int ROLE_ALREADY_EXISTS = 71; + + /** + * Privilege is invalid. + */ + public static final int INVALID_PRIVILEGE = 72; + + /** + * Invalid IP address whitelist. + */ + public static final int INVALID_WHITELIST = 73; + + /** + * Quotas not enabled on server. + */ + public static final int QUOTAS_NOT_ENABLED = 74; + + /** + * Invalid quota value. + */ + public static final int INVALID_QUOTA = 75; + + /** + * User must be authentication before performing database operations. + */ + public static final int NOT_AUTHENTICATED = 80; + + /** + * User does not possess the required role to perform the database operation. + */ + public static final int ROLE_VIOLATION = 81; + + /** + * Command not allowed because sender IP address not whitelisted. + */ + public static final int NOT_WHITELISTED = 82; + + /** + * Quota exceeded. + */ + public static final int QUOTA_EXCEEDED = 83; + + /** + * A user defined function returned an error code. + */ + public static final int UDF_BAD_RESPONSE = 100; + + /** + * Transaction record blocked by a different transaction. + */ + public static final int MRT_BLOCKED = 120; + + /** + * Transaction read version mismatch identified during commit. + * Some other command changed the record outside of the transaction. + */ + public static final int MRT_VERSION_MISMATCH = 121; + + /** + * Transaction deadline reached without a successful commit or abort. + */ + public static final int MRT_EXPIRED = 122; + + /** + * Transaction write command limit (4096) exceeded. + */ + public static final int MRT_TOO_MANY_WRITES = 123; + + /** + * Transaction was already committed. + */ + public static final int MRT_COMMITTED = 124; + + /** + * Transaction was already aborted. + */ + public static final int MRT_ABORTED = 125; + + /** + * This record has been locked by a previous update in this transaction. + */ + public static final int MRT_ALREADY_LOCKED = 126; + + /** + * This transaction has already started. Writing to the same transaction with independent threads is unsafe. + */ + public static final int MRT_MONITOR_EXISTS = 127; + + /** + * Batch functionality has been disabled. + */ + public static final int BATCH_DISABLED = 150; + + /** + * Batch max requests have been exceeded. + */ + public static final int BATCH_MAX_REQUESTS_EXCEEDED = 151; + + /** + * All batch queues are full. + */ + public static final int BATCH_QUEUES_FULL = 152; + + /** + * Secondary index already exists. + */ + public static final int INDEX_ALREADY_EXISTS = 200; + public static final int INDEX_FOUND = 200; // For legacy reasons. + + /** + * Requested secondary index does not exist. + */ + public static final int INDEX_NOTFOUND = 201; + + /** + * Secondary index memory space exceeded. + */ + public static final int INDEX_OOM = 202; + + /** + * Secondary index not available. + */ + public static final int INDEX_NOTREADABLE = 203; + + /** + * Generic secondary index error. + */ + public static final int INDEX_GENERIC = 204; + + /** + * Index name maximum length exceeded. + */ + public static final int INDEX_NAME_MAXLEN = 205; + + /** + * Maximum number of indicies exceeded. + */ + public static final int INDEX_MAXCOUNT = 206; + + /** + * Secondary index query aborted. + */ + public static final int QUERY_ABORTED = 210; + + /** + * Secondary index queue full. + */ + public static final int QUERY_QUEUEFULL = 211; + + /** + * Secondary index query timed out on server. + */ + public static final int QUERY_TIMEOUT = 212; + + /** + * Generic query error. + */ + public static final int QUERY_GENERIC = 213; + + /** + * Should connection be put back into pool. + */ + public static boolean keepConnection(int resultCode) { + if (resultCode <= 0) { + // Do not keep connection on client errors. + return false; + } + + switch (resultCode) { + case SCAN_ABORT: + case QUERY_ABORTED: + return false; + + default: + // Keep connection on TIMEOUT because it can be server response which does not + // close socket. Also, client timeout code path does not call this method. + return true; + } + } + + /** + * Return result code as a string. + */ + public static String getResultString(int resultCode) { + switch (resultCode) { + case TXN_ALREADY_ABORTED: + return "Transaction already aborted"; + + case TXN_ALREADY_COMMITTED: + return "Transaction already committed"; + + case TXN_FAILED: + return "Transaction failed"; + + case BATCH_FAILED: + return "One or more keys failed in a batch"; + + case NO_RESPONSE: + return "No response received from server"; + + case MAX_ERROR_RATE: + return "Max error rate exceeded"; + + case MAX_RETRIES_EXCEEDED: + return "Max retries exceeded"; + + case SERIALIZE_ERROR: + return "Serialize error"; + + case ASYNC_QUEUE_FULL: + return "Async delay queue is full"; + + case SERVER_NOT_AVAILABLE: + return "Connection to server failed"; + + case NO_MORE_CONNECTIONS: + return "No more available connections"; + + case QUERY_TERMINATED: + return "Query terminated"; + + case SCAN_TERMINATED: + return "Scan terminated"; + + case INVALID_NODE_ERROR: + return "Invalid node"; + + case PARSE_ERROR: + return "Parse error"; + + case CLIENT_ERROR: + return "Client error"; + + case OK: + return "ok"; + + case SERVER_ERROR: + return "Server error"; + + case KEY_NOT_FOUND_ERROR: + return "Key not found"; + + case GENERATION_ERROR: + return "Generation error"; + + case PARAMETER_ERROR: + return "Parameter error"; + + case KEY_EXISTS_ERROR: + return "Key already exists"; + + case BIN_EXISTS_ERROR: + return "Bin already exists"; + + case CLUSTER_KEY_MISMATCH: + return "Cluster key mismatch"; + + case SERVER_MEM_ERROR: + return "Server memory error"; + + case TIMEOUT: + return "Timeout"; + + case ALWAYS_FORBIDDEN: + return "Operation not allowed"; + + case PARTITION_UNAVAILABLE: + return "Partition unavailable"; + + case BIN_TYPE_ERROR: + return "Bin type error"; + + case RECORD_TOO_BIG: + return "Record too big"; + + case KEY_BUSY: + return "Hot key"; + + case SCAN_ABORT: + return "Scan aborted"; + + case UNSUPPORTED_FEATURE: + return "Unsupported Server Feature"; + + case BIN_NOT_FOUND: + return "Bin not found"; + + case DEVICE_OVERLOAD: + return "Device overload"; + + case KEY_MISMATCH: + return "Key mismatch"; + + case INVALID_NAMESPACE: + return "Namespace not found"; + + case BIN_NAME_TOO_LONG: + return "Bin name length greater than 15 characters or maximum bins exceeded"; + + case FAIL_FORBIDDEN: + return "Operation not allowed at this time"; + + case ELEMENT_NOT_FOUND: + return "Map key not found"; + + case ELEMENT_EXISTS: + return "Map key exists"; + + case ENTERPRISE_ONLY: + return "Enterprise only"; + + case OP_NOT_APPLICABLE: + return "Operation not applicable"; + + case FILTERED_OUT: + return "Command filtered out"; + + case LOST_CONFLICT: + return "Command failed due to conflict with XDR"; + + case XDR_KEY_BUSY: + return "Write can't complete until XDR finishes shipping"; + + case QUERY_END: + return "Query end"; + + case SECURITY_NOT_SUPPORTED: + return "Security not supported"; + + case SECURITY_NOT_ENABLED: + return "Security not enabled"; + + case SECURITY_SCHEME_NOT_SUPPORTED: + return "Security scheme not supported"; + + case INVALID_COMMAND: + return "Invalid command"; + + case INVALID_FIELD: + return "Invalid field"; + + case ILLEGAL_STATE: + return "Illegal state"; + + case INVALID_USER: + return "Invalid user"; + + case USER_ALREADY_EXISTS: + return "User already exists"; + + case INVALID_PASSWORD: + return "Invalid password"; + + case EXPIRED_PASSWORD: + return "Password expired"; + + case FORBIDDEN_PASSWORD: + return "Password can't be reused"; + + case INVALID_CREDENTIAL: + return "Invalid credential"; + + case EXPIRED_SESSION: + return "Login session expired"; + + case INVALID_ROLE: + return "Invalid role"; + + case ROLE_ALREADY_EXISTS: + return "Role already exists"; + + case INVALID_PRIVILEGE: + return "Invalid privilege"; + + case INVALID_WHITELIST: + return "Invalid whitelist"; + + case QUOTAS_NOT_ENABLED: + return "Quotas not enabled"; + + case INVALID_QUOTA: + return "Invalid quota"; + + case NOT_AUTHENTICATED: + return "Not authenticated"; + + case ROLE_VIOLATION: + return "Role violation"; + + case NOT_WHITELISTED: + return "Command not whitelisted"; + + case QUOTA_EXCEEDED: + return "Quota exceeded"; + + case UDF_BAD_RESPONSE: + return "UDF returned error"; + + case MRT_BLOCKED: + return "Transaction record blocked by a different transaction"; + + case MRT_VERSION_MISMATCH: + return "Transaction version mismatch"; + + case MRT_EXPIRED: + return "Transaction expired"; + + case MRT_TOO_MANY_WRITES: + return "Transaction write command limit exceeded"; + + case MRT_COMMITTED: + return "Transaction already committed"; + + case MRT_ABORTED: + return "Transaction already aborted"; + + case MRT_ALREADY_LOCKED: + return "This record has been locked by a previous update in this transaction"; + + case MRT_MONITOR_EXISTS: + return "This transaction has already started. Writing to the same transaction with independent threads is unsafe"; + + case BATCH_DISABLED: + return "Batch functionality has been disabled"; + + case BATCH_MAX_REQUESTS_EXCEEDED: + return "Batch max requests have been exceeded"; + + case BATCH_QUEUES_FULL: + return "All batch queues are full"; + + case INDEX_ALREADY_EXISTS: + return "Index already exists"; + + case INDEX_NOTFOUND: + return "Index not found"; + + case INDEX_OOM: + return "Index out of memory"; + + case INDEX_NOTREADABLE: + return "Index not readable"; + + case INDEX_GENERIC: + return "Index error"; + + case INDEX_NAME_MAXLEN: + return "Index name max length exceeded"; + + case INDEX_MAXCOUNT: + return "Index count exceeds max"; + + case QUERY_ABORTED: + return "Query aborted"; + + case QUERY_QUEUEFULL: + return "Query queue full"; + + case QUERY_TIMEOUT: + return "Query timeout"; + + case QUERY_GENERIC: + return "Query error"; + + default: + return ""; + } + } +} diff --git a/src/main/java/com/aerospike/dsl/client/Value.java b/src/main/java/com/aerospike/dsl/client/Value.java new file mode 100644 index 0000000..600ba33 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/Value.java @@ -0,0 +1,1647 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client; + +import com.aerospike.dsl.client.cdt.MapOrder; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.command.Buffer; +import com.aerospike.dsl.client.command.ParticleType; +import com.aerospike.dsl.client.util.Packer; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.UUID; + +/** + * Polymorphic value classes used to efficiently serialize objects into the wire protocol. + */ +public abstract class Value { + /** + * Should client send boolean particle type for a boolean bin. If false, + * an integer particle type (1 or 0) is sent instead. Must be false for server + * versions less than 5.6 which do not support boolean bins. Can set to true for + * server 5.6+. + */ + public static boolean UseBoolBin = true; + + /** + * Should the client return a map when {@link MapReturnType#KEY_VALUE} + * is specified in a map read operation and the server returns a list of key/value pairs. + */ + public static boolean ReturnMapForKeyValue = false; + + /** + * Null value. + */ + public static final Value NULL = NullValue.INSTANCE; + + /** + * Infinity value to be used in CDT range comparisons only. + */ + public static final Value INFINITY = new InfinityValue(); + + /** + * Wildcard value to be used in CDT range comparisons only. + */ + public static final Value WILDCARD = new WildcardValue(); + + /** + * Get string or null value instance. + */ + public static Value get(String value) { + return (value == null)? NullValue.INSTANCE : new StringValue(value); + } + + /** + * Get byte array or null value instance. + */ + public static Value get(byte[] value) { + return (value == null)? NullValue.INSTANCE : new BytesValue(value); + } + + /** + * Get byte array with type or null value instance. + */ + public static Value get(byte[] value, int type) { + return (value == null)? NullValue.INSTANCE : new BytesValue(value, type); + } + + /** + * Get byte segment or null value instance. + */ + public static Value get(byte[] value, int offset, int length) { + return (value == null)? NullValue.INSTANCE : new ByteSegmentValue(value, offset, length); + } + + /** + * Get byte segment or null value instance. + */ + public static Value get(ByteBuffer bb) { + return (bb == null)? NullValue.INSTANCE : new BytesValue(bb.array()); + } + + /** + * Get byte value instance. + */ + public static Value get(byte value) { + return new ByteValue(value); + } + + /** + * Get short value instance. + */ + public static Value get(short value) { + return new ShortValue(value); + } + + /** + * Get integer value instance. + */ + public static Value get(int value) { + return new IntegerValue(value); + } + + /** + * Get long value instance. + */ + public static Value get(long value) { + return new LongValue(value); + } + + /** + * Get double value instance. + */ + public static Value get(double value) { + return new DoubleValue(value); + } + + /** + * Get float value instance. + */ + public static Value get(float value) { + return new FloatValue(value); + } + + /** + * Get boolean value instance. + */ + public static Value get(boolean value) { + if (UseBoolBin) { + return new BooleanValue(value); + } + else { + return new BoolIntValue(value); + } + } + + /** + * Get enum value string instance. + */ + public static Value get(Enum value) { + return (value == null)? NullValue.INSTANCE : new StringValue(value.toString()); + } + + /** + * Get UUID value string instance. + */ + public static Value get(UUID value) { + return (value == null)? NullValue.INSTANCE : new StringValue(value.toString()); + } + + /** + * Get list or null value instance. + */ + public static Value get(List value) { + return (value == null)? NullValue.INSTANCE : new ListValue(value); + } + + /** + * Get map or null value instance. + */ + public static Value get(Map value) { + return (value == null)? NullValue.INSTANCE : new MapValue(value); + } + + /** + * Get sorted map or null value instance. + */ + public static Value get(SortedMap value) { + return (value == null)? NullValue.INSTANCE : new MapValue(value, MapOrder.KEY_ORDERED); + } + + /** + * This method is deprecated. + * Use {@link #get(Map)} if the map is unsorted (like HashMap). + * Use {@link #get(SortedMap)} if the map is sorted (like TreeMap). + *

+ * Get map or null value instance. + */ + @Deprecated + public static Value get(Map value, MapOrder order) { + return (value == null)? NullValue.INSTANCE : new MapValue(value, order); + } + + /** + * Get sorted map or null value instance. + */ + public static Value get(List> value, MapOrder mapOrder) { + return (value == null)? NullValue.INSTANCE : new SortedMapValue(value, mapOrder); + } + + /** + * Get value array instance. + */ + public static Value get(Value[] value) { + return (value == null)? NullValue.INSTANCE : new ValueArray(value); + } + + /** + * Get GeoJSON or null value instance. + */ + public static Value getAsGeoJSON(String value) { + return (value == null)? NullValue.INSTANCE : new GeoJSONValue(value); + } + + /** + * Get HyperLogLog or null value instance. + */ + public static Value getAsHLL(byte[] value) { + return (value == null)? NullValue.INSTANCE : new HLLValue(value); + } + + /** + * Get null value instance. + */ + public static Value getAsNull() { + return NullValue.INSTANCE; + } + + /** + * Determine value given generic object. + * This is the slowest of the Value get() methods. + * Useful when copying records from one cluster to another. + */ + public static Value get(Object value) { + if (value == null) { + return NullValue.INSTANCE; + } + + if (value instanceof Value) { + return (Value)value; + } + + if (value instanceof byte[]) { + return new BytesValue((byte[])value); + } + + if (value instanceof String) { + return new StringValue((String)value); + } + + if (value instanceof Integer) { + return new IntegerValue((Integer)value); + } + + if (value instanceof Long) { + return new LongValue((Long)value); + } + + if (value instanceof List) { + return new ListValue((List)value); + } + + if (value instanceof Map) { + return new MapValue((Map)value); + } + + if (value instanceof Double) { + return new DoubleValue((Double)value); + } + + if (value instanceof Float) { + return new FloatValue((Float)value); + } + + if (value instanceof Short) { + return new ShortValue((Short)value); + } + + if (value instanceof Boolean) { + if (UseBoolBin) { + return new BooleanValue((Boolean)value); + } + else { + return new BoolIntValue((Boolean)value); + } + } + + if (value instanceof Byte) { + return new ByteValue((byte)value); + } + + if (value instanceof Character) { + return Value.get(((Character)value).charValue()); + } + + if (value instanceof Enum) { + return new StringValue(value.toString()); + } + + if (value instanceof UUID) { + return new StringValue(value.toString()); + } + + if (value instanceof ByteBuffer) { + ByteBuffer bb = (ByteBuffer)value; + return new BytesValue(bb.array()); + } + + throw new AerospikeException("Unsupported type: " + value.getClass().getName()); + } + + /** + * Get value from Record object. Useful when copying records from one cluster to another. + * @deprecated + *

Use {@link Value#get(Object)} instead. + */ + @Deprecated + public static Value getFromRecordObject(Object value) { + return Value.get(value); + } + + /** + * Calculate number of bytes necessary to serialize the value in the wire protocol. + */ + public abstract int estimateSize() throws AerospikeException; + + /** + * Serialize the value in the wire protocol. + */ + public abstract int write(byte[] buffer, int offset) throws AerospikeException; + + /** + * Serialize the value using MessagePack. + */ + public abstract void pack(Packer packer); + + /** + * Validate if value type can be used as a key. + * @throws AerospikeException if type can't be used as a key. + */ + public void validateKeyType() throws AerospikeException { + } + + /** + * Get wire protocol value type. + */ + public abstract int getType(); + + /** + * Return original value as an Object. + */ + public abstract Object getObject(); + + /** + * Return value as an integer. + */ + public int toInteger() { + return 0; + } + + /** + * Return value as a long. + */ + public long toLong() { + return 0; + } + + /** + * Empty value. + */ + public static final class NullValue extends Value { + public static final NullValue INSTANCE = new NullValue(); + + @Override + public int estimateSize() { + return 0; + } + + @Override + public int write(byte[] buffer, int offset) { + return 0; + } + + @Override + public void pack(Packer packer) { + packer.packNil(); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: null"); + } + + @Override + public int getType() { + return ParticleType.NULL; + } + + @Override + public Object getObject() { + return null; + } + + @Override + public String toString() { + return null; + } + + @Override + public boolean equals(Object other) { + if (other == null) { + return true; + } + return this.getClass().equals(other.getClass()); + } + + @Override + public final int hashCode() { + return 0; + } + } + + /** + * Byte array value. + */ + public static final class BytesValue extends Value { + private final byte[] bytes; + private final int type; + + public BytesValue(byte[] bytes) { + this.bytes = bytes; + this.type = ParticleType.BLOB; + } + + public BytesValue(byte[] bytes, int type) { + this.bytes = bytes; + this.type = type; + } + + @Override + public int estimateSize() { + return bytes.length; + } + + @Override + public int write(byte[] buffer, int offset) { + System.arraycopy(bytes, 0, buffer, offset, bytes.length); + return bytes.length; + } + + @Override + public void pack(Packer packer) { + packer.packParticleBytes(bytes, type); + } + + @Override + public int getType() { + return type; + } + + @Override + public Object getObject() { + return bytes; + } + + @Override + public String toString() { + return Buffer.bytesToHexString(bytes); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + Arrays.equals(this.bytes, ((BytesValue)other).bytes)); + } + + @Override + public int hashCode() { + return Arrays.hashCode(bytes); + } + } + + /** + * Byte segment value. + */ + public static final class ByteSegmentValue extends Value { + private final byte[] bytes; + private final int offset; + private final int length; + + public ByteSegmentValue(byte[] bytes, int offset, int length) { + this.bytes = bytes; + this.offset = offset; + this.length = length; + } + + @Override + public int estimateSize() { + return length; + + } + + @Override + public int write(byte[] buffer, int targetOffset) { + System.arraycopy(bytes, offset, buffer, targetOffset, length); + return length; + } + + @Override + public void pack(Packer packer) { + packer.packParticleBytes(bytes, offset, length); + } + + @Override + public int getType() { + return ParticleType.BLOB; + } + + @Override + public Object getObject() { + return this; + } + + @Override + public String toString() { + return Buffer.bytesToHexString(bytes, offset, length); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + + if (! this.getClass().equals(obj.getClass())) { + return false; + } + ByteSegmentValue other = (ByteSegmentValue)obj; + + if (this.length != other.length) { + return false; + } + + for (int i = 0; i < length; i++) { + if (this.bytes[this.offset + i] != other.bytes[other.offset + i]) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + int result = 1; + for (int i = 0; i < length; i++) { + result = 31 * result + bytes[offset+i]; + } + return result; + } + + public byte[] getBytes() { + return bytes; + } + + public int getOffset() { + return offset; + } + + public int getLength() { + return length; + } + } + + /** + * Byte value. + */ + public static final class ByteValue extends Value { + private final byte value; + + public ByteValue(byte value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 8; + } + + @Override + public int write(byte[] buffer, int offset) { + Buffer.longToBytes(value & 0xff, buffer, offset); + return 8; + } + + @Override + public void pack(Packer packer) { + packer.packInt(value & 0xff); + } + + @Override + public int getType() { + // The server does not natively handle one byte, so store as long (8 byte integer). + return ParticleType.INTEGER; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value & 0xff); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((ByteValue)other).value); + } + + @Override + public int hashCode() { + return value & 0xff; + } + + @Override + public int toInteger() { + return value & 0xff; + } + + @Override + public long toLong() { + return value & 0xff; + } + } + + /** + * String value. + */ + public static final class StringValue extends Value { + private final String value; + + public StringValue(String value) { + this.value = value; + } + + @Override + public int estimateSize() { + return Buffer.estimateSizeUtf8(value); + } + + @Override + public int write(byte[] buffer, int offset) { + return Buffer.stringToUtf8(value, buffer, offset); + } + + @Override + public void pack(Packer packer) { + packer.packParticleString(value); + } + + @Override + public int getType() { + return ParticleType.STRING; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value.equals(((StringValue)other).value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + } + + /** + * Short value. + */ + public static final class ShortValue extends Value { + private final short value; + + public ShortValue(short value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 8; + } + + @Override + public int write(byte[] buffer, int offset) { + Buffer.longToBytes(value, buffer, offset); + return 8; + } + + @Override + public void pack(Packer packer) { + packer.packInt(value); + } + + @Override + public int getType() { + return ParticleType.INTEGER; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Short.toString(value); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((ShortValue)other).value); + } + + @Override + public int hashCode() { + return value; + } + + @Override + public int toInteger() { + return value; + } + + @Override + public long toLong() { + return value; + } + } + + /** + * Integer value. + */ + public static final class IntegerValue extends Value { + private final int value; + + public IntegerValue(int value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 8; + } + + @Override + public int write(byte[] buffer, int offset) { + Buffer.longToBytes(value, buffer, offset); + return 8; + } + + @Override + public void pack(Packer packer) { + packer.packInt(value); + } + + @Override + public int getType() { + return ParticleType.INTEGER; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Integer.toString(value); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((IntegerValue)other).value); + } + + @Override + public int hashCode() { + return value; + } + + @Override + public int toInteger() { + return value; + } + + @Override + public long toLong() { + return value; + } + } + + /** + * Long value. + */ + public static final class LongValue extends Value { + private final long value; + + public LongValue(long value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 8; + } + + @Override + public int write(byte[] buffer, int offset) { + Buffer.longToBytes(value, buffer, offset); + return 8; + } + + @Override + public void pack(Packer packer) { + packer.packLong(value); + } + + @Override + public int getType() { + return ParticleType.INTEGER; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Long.toString(value); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((LongValue)other).value); + } + + @Override + public int hashCode() { + return (int)(value ^ (value >>> 32)); + } + + @Override + public int toInteger() { + return (int)value; + } + + @Override + public long toLong() { + return value; + } + } + + /** + * Double value. + */ + public static final class DoubleValue extends Value { + private final double value; + + public DoubleValue(double value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 8; + } + + @Override + public int write(byte[] buffer, int offset) { + Buffer.doubleToBytes(value, buffer, offset); + return 8; + } + + @Override + public void pack(Packer packer) { + packer.packDouble(value); + } + + @Override + public int getType() { + return ParticleType.DOUBLE; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Double.toString(value); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((DoubleValue)other).value); + } + + @Override + public int hashCode() { + long bits = Double.doubleToLongBits(value); + return (int)(bits ^ (bits >>> 32)); + } + + @Override + public int toInteger() { + return (int)value; + } + + @Override + public long toLong() { + return (long)value; + } + } + + /** + * Float value. + */ + public static final class FloatValue extends Value { + private final float value; + + public FloatValue(float value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 8; + } + + @Override + public int write(byte[] buffer, int offset) { + Buffer.doubleToBytes(value, buffer, offset); + return 8; + } + + @Override + public void pack(Packer packer) { + packer.packFloat(value); + } + + @Override + public int getType() { + return ParticleType.DOUBLE; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Float.toString(value); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((FloatValue)other).value); + } + + @Override + public int hashCode() { + return Float.floatToIntBits(value); + } + + @Override + public int toInteger() { + return (int)value; + } + + @Override + public long toLong() { + return (long)value; + } + } + + /** + * Boolean value. + */ + public static final class BooleanValue extends Value { + private final boolean value; + + public BooleanValue(boolean value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 1; + } + + @Override + public int write(byte[] buffer, int offset) { + buffer[offset] = value? (byte)1 : (byte)0; + return 1; + } + + @Override + public void pack(Packer packer) { + packer.packBoolean(value); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: boolean"); + } + + @Override + public int getType() { + return ParticleType.BOOL; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Boolean.toString(value); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((BooleanValue)other).value); + } + + @Override + public int hashCode() { + return value ? 1231 : 1237; + } + + @Override + public int toInteger() { + return value? 1 : 0; + } + + @Override + public long toLong() { + return value? 1L : 0L; + } + } + + /** + * Boolean value that converts to integer when sending a bin to the server. + * This class will be deleted once full conversion to boolean particle type + * is complete. + */ + public static final class BoolIntValue extends Value { + private final boolean value; + + public BoolIntValue(boolean value) { + this.value = value; + } + + @Override + public int estimateSize() { + return 8; + } + + @Override + public int write(byte[] buffer, int offset) { + Buffer.longToBytes(value? 1L : 0L, buffer, offset); + return 8; + } + + @Override + public void pack(Packer packer) { + packer.packBoolean(value); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: BoolIntValue"); + } + + @Override + public int getType() { + // The server does not natively handle boolean, so store as long (8 byte integer). + return ParticleType.INTEGER; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return Boolean.toString(value); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value == ((BoolIntValue)other).value); + } + + @Override + public int hashCode() { + return value ? 1231 : 1237; + } + + @Override + public int toInteger() { + return value? 1 : 0; + } + + @Override + public long toLong() { + return value? 1L : 0L; + } + } + + /** + * GeoJSON value. + */ + public static final class GeoJSONValue extends Value { + private final String value; + + public GeoJSONValue(String value) { + this.value = value; + } + + @Override + public int estimateSize() { + // flags + ncells + jsonstr + return 1 + 2 + Buffer.estimateSizeUtf8(value); + } + + @Override + public int write(byte[] buffer, int offset) { + buffer[offset] = 0; // flags + Buffer.shortToBytes(0, buffer, offset + 1); // ncells + return 1 + 2 + Buffer.stringToUtf8(value, buffer, offset + 3); // jsonstr + } + + @Override + public void pack(Packer packer) { + packer.packGeoJSON(value); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: GeoJson"); + } + + @Override + public int getType() { + return ParticleType.GEOJSON; + } + + @Override + public Object getObject() { + return value; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.value.equals(((GeoJSONValue)other).value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } + } + + /** + * HyperLogLog value. + */ + public static final class HLLValue extends Value { + private final byte[] bytes; + + public HLLValue(byte[] bytes) { + this.bytes = bytes; + } + + @Override + public int estimateSize() { + return bytes.length; + } + + @Override + public int write(byte[] buffer, int offset) { + System.arraycopy(bytes, 0, buffer, offset, bytes.length); + return bytes.length; + } + + @Override + public void pack(Packer packer) { + packer.packParticleBytes(bytes); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: HLL"); + } + + @Override + public int getType() { + return ParticleType.HLL; + } + + @Override + public Object getObject() { + return bytes; + } + + public byte[] getBytes() { + return bytes; + } + + @Override + public String toString() { + return Buffer.bytesToHexString(bytes); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + Arrays.equals(this.bytes, ((HLLValue)other).bytes)); + } + + @Override + public int hashCode() { + return Arrays.hashCode(bytes); + } + } + + /** + * Value array. + */ + public static final class ValueArray extends Value { + private final Value[] array; + private byte[] bytes; + + public ValueArray(Value[] array) { + this.array = array; + } + + @Override + public int estimateSize() throws AerospikeException { + bytes = Packer.pack(array); + return bytes.length; + } + + @Override + public int write(byte[] buffer, int offset) { + System.arraycopy(bytes, 0, buffer, offset, bytes.length); + return bytes.length; + } + + @Override + public void pack(Packer packer) { + packer.packValueArray(array); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: value[]"); + } + + @Override + public int getType() { + return ParticleType.LIST; + } + + @Override + public Object getObject() { + return array; + } + + @Override + public String toString() { + return Arrays.toString(array); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + Arrays.equals(this.array, ((ValueArray)other).array)); + } + + @Override + public int hashCode() { + return Arrays.hashCode(array); + } + } + + /** + * List value. + */ + public static final class ListValue extends Value { + private final List list; + private byte[] bytes; + + public ListValue(List list) { + this.list = list; + } + + @Override + public int estimateSize() throws AerospikeException { + bytes = Packer.pack(list); + return bytes.length; + } + + @Override + public int write(byte[] buffer, int offset) { + System.arraycopy(bytes, 0, buffer, offset, bytes.length); + return bytes.length; + } + + @Override + public void pack(Packer packer) { + packer.packList(list); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: list"); + } + + @Override + public int getType() { + return ParticleType.LIST; + } + + @Override + public Object getObject() { + return list; + } + + @Override + public String toString() { + return list.toString(); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.list.equals(((ListValue)other).list)); + } + + @Override + public int hashCode() { + return list.hashCode(); + } + } + + /** + * Map value. + */ + public static final class MapValue extends Value { + private final Map map; + private final MapOrder order; + private byte[] bytes; + + public MapValue(Map map) { + this.map = map; + this.order = getMapOrder(map); + } + + public MapValue(Map map, MapOrder order) { + this.map = map; + this.order = order; + } + + @Override + public int estimateSize() throws AerospikeException { + bytes = Packer.pack(map, order); + return bytes.length; + } + + @Override + public int write(byte[] buffer, int offset) { + System.arraycopy(bytes, 0, buffer, offset, bytes.length); + return bytes.length; + } + + @Override + public void pack(Packer packer) { + packer.packMap(map, order); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: map"); + } + + @Override + public int getType() { + return ParticleType.MAP; + } + + @Override + public Object getObject() { + return map; + } + + @Override + public String toString() { + return map.toString(); + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass()) && + this.map.equals(((MapValue)other).map)); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + public static MapOrder getMapOrder(Map map) { + return (map instanceof SortedMap)? MapOrder.KEY_ORDERED : MapOrder.UNORDERED; + } + } + + /** + * Sorted map value. + */ + public static final class SortedMapValue extends Value { + private final List> list; + private byte[] bytes; + private final MapOrder order; + + public SortedMapValue(List> list, MapOrder order) { + this.list = list; + this.order = order; + } + + @Override + public int estimateSize() throws AerospikeException { + bytes = Packer.pack(list, order); + return bytes.length; + } + + @Override + public int write(byte[] buffer, int offset) { + System.arraycopy(bytes, 0, buffer, offset, bytes.length); + return bytes.length; + } + + @Override + public void pack(Packer packer) { + packer.packMap(list, order); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: map"); + } + + @Override + public int getType() { + return ParticleType.MAP; + } + + @Override + public Object getObject() { + return list; + } + + @Override + public String toString() { + return list.toString(); + } + + @Override + public boolean equals(Object other) { + if (other == null || ! this.getClass().equals(other.getClass())) { + return false; + } + SortedMapValue o = (SortedMapValue)other; + return this.order == o.order && this.list.equals(o.list); + } + + @Override + public int hashCode() { + return list.hashCode(); + } + } + + /** + * Infinity value. + */ + public static final class InfinityValue extends Value { + @Override + public int estimateSize() { + return 0; + } + + @Override + public int write(byte[] buffer, int offset) { + return 0; + } + + @Override + public void pack(Packer packer) { + packer.packInfinity(); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: INF"); + } + + @Override + public int getType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid particle type: INF"); + } + + @Override + public Object getObject() { + return null; + } + + @Override + public String toString() { + return "INF"; + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass())); + } + + @Override + public final int hashCode() { + return 0; + } + } + + /** + * Wildcard value. + */ + public static final class WildcardValue extends Value { + @Override + public int estimateSize() { + return 0; + } + + @Override + public int write(byte[] buffer, int offset) { + return 0; + } + + @Override + public void pack(Packer packer) { + packer.packWildcard(); + } + + @Override + public void validateKeyType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid key type: wildcard"); + } + + @Override + public int getType() { + throw new AerospikeException(ResultCode.PARAMETER_ERROR, "Invalid particle type: wildcard"); + } + + @Override + public Object getObject() { + return null; + } + + @Override + public String toString() { + return "*"; + } + + @Override + public boolean equals(Object other) { + return (other != null && + this.getClass().equals(other.getClass())); + } + + @Override + public final int hashCode() { + return 0; + } + } +} diff --git a/src/main/java/com/aerospike/dsl/client/cdt/CTX.java b/src/main/java/com/aerospike/dsl/client/cdt/CTX.java new file mode 100644 index 0000000..e6554ff --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/cdt/CTX.java @@ -0,0 +1,168 @@ +/* + * Copyright 2012-2022 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.cdt; + +import com.aerospike.dsl.client.AerospikeException; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.util.Crypto; +import com.aerospike.dsl.client.util.Pack; +import com.aerospike.dsl.client.util.Unpacker; + +import java.util.List; + +/** + * Nested CDT context. Identifies the location of nested list/map to apply the operation. + * for the current level. An array of CTX identifies location of the list/map on multiple + * levels on nesting. + */ +public final class CTX { + /** + * Lookup list by index offset. + *

+ * If the index is negative, the resolved index starts backwards from end of list. + * If an index is out of bounds, a parameter error will be returned. Examples: + *

    + *
  • 0: First item.
  • + *
  • 4: Fifth item.
  • + *
  • -1: Last item.
  • + *
  • -3: Third to last item.
  • + *
+ */ + public static CTX listIndex(int index) { + return new CTX(0x10, Value.get(index)); + } + + /** + * Lookup list by rank. + *
    + *
  • 0 = smallest value
  • + *
  • N = Nth smallest value
  • + *
  • -1 = largest value
  • + *
+ */ + public static CTX listRank(int rank) { + return new CTX(0x11, Value.get(rank)); + } + + /** + * Lookup list by value. + */ + public static CTX listValue(Value key) { + return new CTX(0x13, key); + } + + /** + * Lookup map by index offset. + *

+ * If the index is negative, the resolved index starts backwards from end of list. + * If an index is out of bounds, a parameter error will be returned. Examples: + *

    + *
  • 0: First item.
  • + *
  • 4: Fifth item.
  • + *
  • -1: Last item.
  • + *
  • -3: Third to last item.
  • + *
+ */ + public static CTX mapIndex(int index) { + return new CTX(0x20, Value.get(index)); + } + + /** + * Lookup map by rank. + *
    + *
  • 0 = smallest value
  • + *
  • N = Nth smallest value
  • + *
  • -1 = largest value
  • + *
+ */ + public static CTX mapRank(int rank) { + return new CTX(0x21, Value.get(rank)); + } + + /** + * Lookup map by key. + */ + public static CTX mapKey(Value key) { + return new CTX(0x22, key); + } + + /** + * Lookup map by value. + */ + public static CTX mapValue(Value key) { + return new CTX(0x23, key); + } + + /** + * Serialize context array to bytes. + */ + public static byte[] toBytes(CTX[] ctx) { + return Pack.pack(ctx); + } + + /** + * Deserialize bytes to context array. + */ + public static CTX[] fromBytes(byte[] bytes) { + List list = (List) Unpacker.unpackObjectList(bytes, 0, bytes.length); + int max = list.size(); + CTX[] ctx = new CTX[max / 2]; + int i = 0; + int count = 0; + + while (i < max) { + int id = (int) (long) (Long) list.get(i); + + if (++i >= max) { + throw new AerospikeException.Parse("List count must be even"); + } + + + Object obj = list.get(i); + Value val = Value.get(obj); + + ctx[count++] = new CTX(id, val); + i++; + } + return ctx; + } + + /** + * Serialize context array to base64 encoded string. + */ + public static String toBase64(CTX[] ctx) { + byte[] bytes = Pack.pack(ctx); + return Crypto.encodeBase64(bytes); + } + + /** + * Deserialize base64 encoded string to context array. + */ + public static CTX[] fromBase64(String base64) { + byte[] b64 = base64.getBytes(); + byte[] bytes = Crypto.decodeBase64(b64, 0, b64.length); + return fromBytes(bytes); + } + + public final int id; + public final Value value; + + private CTX(int id, Value value) { + this.id = id; + this.value = value; + } +} diff --git a/src/main/java/com/aerospike/dsl/client/cdt/ListReturnType.java b/src/main/java/com/aerospike/dsl/client/cdt/ListReturnType.java new file mode 100644 index 0000000..0d098eb --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/cdt/ListReturnType.java @@ -0,0 +1,88 @@ +/* + * Copyright 2012-2022 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.cdt; + +/** + * List return type. Type of data to return when selecting or removing items from the list. + */ +public final class ListReturnType { + /** + * Do not return a result. + */ + public static final int NONE = 0; + + /** + * Return index offset order. + *
    + *
  • 0 = first key
  • + *
  • N = Nth key
  • + *
  • -1 = last key
  • + *
+ */ + public static final int INDEX = 1; + + /** + * Return reverse index offset order. + *
    + *
  • 0 = last key
  • + *
  • -1 = first key
  • + *
+ */ + public static final int REVERSE_INDEX = 2; + + /** + * Return value order. + *
    + *
  • 0 = smallest value
  • + *
  • N = Nth smallest value
  • + *
  • -1 = largest value
  • + *
+ */ + public static final int RANK = 3; + + /** + * Return reverse value order. + *
    + *
  • 0 = largest value
  • + *
  • N = Nth largest value
  • + *
  • -1 = smallest value
  • + *
+ */ + public static final int REVERSE_RANK = 4; + + /** + * Return count of items selected. + */ + public static final int COUNT = 5; + + /** + * Return value for single key read and value list for range read. + */ + public static final int VALUE = 7; + + /** + * Return true if count > 0. + */ + public static final int EXISTS = 13; + + /** + * Invert meaning of list command and return values. For example: + *
{@code ListOperation.removeByIndexRange(binName, index, count, ListReturnType.VALUE | ListReturnType.INVERTED);}
+ * With the INVERTED flag enabled, the items outside of the specified index range will be removed and returned. + */ + public static final int INVERTED = 0x10000; +} diff --git a/src/main/java/com/aerospike/dsl/client/cdt/MapOrder.java b/src/main/java/com/aerospike/dsl/client/cdt/MapOrder.java new file mode 100644 index 0000000..04c136e --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/cdt/MapOrder.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012-2021 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.cdt; + +import lombok.extern.slf4j.Slf4j; + +/** + * Map storage order. + */ +@Slf4j +public enum MapOrder { + /** + * Map is not ordered. This is the default. + */ + UNORDERED(0, 0x40), + + /** + * Order map by key. + */ + KEY_ORDERED(1, 0x80), + + /** + * Order map by key, then value. + */ + KEY_VALUE_ORDERED(3, 0xc0); + + public final int attributes; + public final int flag; + + private MapOrder(int attributes, int flag) { + this.attributes = attributes; + this.flag = flag; + } +} diff --git a/src/main/java/com/aerospike/dsl/client/cdt/MapReturnType.java b/src/main/java/com/aerospike/dsl/client/cdt/MapReturnType.java new file mode 100644 index 0000000..7148962 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/cdt/MapReturnType.java @@ -0,0 +1,113 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.cdt; + +/** + * Map return type. Type of data to return when selecting or removing items from the map. + */ +public final class MapReturnType { + /** + * Do not return a result. + */ + public static final int NONE = 0; + + /** + * Return key index order. + *
    + *
  • 0 = first key
  • + *
  • N = Nth key
  • + *
  • -1 = last key
  • + *
+ */ + public static final int INDEX = 1; + + /** + * Return reverse key order. + *
    + *
  • 0 = last key
  • + *
  • -1 = first key
  • + *
+ */ + public static final int REVERSE_INDEX = 2; + + /** + * Return value order. + *
    + *
  • 0 = smallest value
  • + *
  • N = Nth smallest value
  • + *
  • -1 = largest value
  • + *
+ */ + public static final int RANK = 3; + + /** + * Return reverse value order. + *
    + *
  • 0 = largest value
  • + *
  • N = Nth largest value
  • + *
  • -1 = smallest value
  • + *
+ */ + public static final int REVERSE_RANK = 4; + + /** + * Return count of items selected. + */ + public static final int COUNT = 5; + + /** + * Return key for single key read and key list for range read. + */ + public static final int KEY = 6; + + /** + * Return value for single key read and value list for range read. + */ + public static final int VALUE = 7; + + /** + * Return key/value items. The possible return types are: + *
    + *
  • HashMap : Returned for unordered maps
  • + *
  • TreeMap : Returned for key ordered maps
  • + *
  • List<Entry> : Returned for range results where range order needs to be preserved.
  • + *
+ */ + public static final int KEY_VALUE = 8; + + /** + * Return true if count > 0. + */ + public static final int EXISTS = 13; + + /** + * Return an unordered map. + */ + public static final int UNORDERED_MAP = 16; + + /** + * Return an ordered map. + */ + public static final int ORDERED_MAP = 17; + + /** + * Invert meaning of map command and return values. For example: + *
{@code MapOperation.removeByKeyRange(binName, keyBegin, keyEnd, MapReturnType.KEY | MapReturnType.INVERTED);}
+ * With the INVERTED flag enabled, the keys outside of the specified key range will be removed and returned. + */ + public static final int INVERTED = 0x10000; +} diff --git a/src/main/java/com/aerospike/dsl/client/command/Buffer.java b/src/main/java/com/aerospike/dsl/client/command/Buffer.java new file mode 100644 index 0000000..8ea063e --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/command/Buffer.java @@ -0,0 +1,643 @@ +/* + * Copyright 2012-2025 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.command; + +import com.aerospike.dsl.client.AerospikeException; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.util.Unpacker; +import com.aerospike.dsl.client.util.Utf8; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +public final class Buffer { + + public static Value bytesToKeyValue(int type, byte[] buf, int offset, int len) + throws AerospikeException { + + switch (type) { + case ParticleType.STRING: + return Value.get(Buffer.utf8ToString(buf, offset, len)); + + case ParticleType.INTEGER: + return bytesToLongValue(buf, offset, len); + + case ParticleType.DOUBLE: + return new Value.DoubleValue(Buffer.bytesToDouble(buf, offset)); + + case ParticleType.BLOB: + return Value.get(Arrays.copyOfRange(buf, offset, offset+len)); + + default: + return null; + } + } + + public static Object bytesToParticle(int type, byte[] buf, int offset, int len) + throws AerospikeException { + + switch (type) { + case ParticleType.STRING: + return Buffer.utf8ToString(buf, offset, len); + + case ParticleType.INTEGER: + return Buffer.bytesToNumber(buf, offset, len); + + case ParticleType.BOOL: + return Buffer.bytesToBool(buf, offset, len); + + case ParticleType.DOUBLE: + return Buffer.bytesToDouble(buf, offset); + + case ParticleType.BLOB: + return Arrays.copyOfRange(buf, offset, offset+len); + + case ParticleType.JBLOB: + // Java deserialization is no longer allowed, so return java serialized blob as a byte[]. + // The user can deserialize the byte[] from the record bin using the following code: + // + // try (ByteArrayInputStream bastream = new ByteArrayInputStream(bytes, 0, len)) { + // try (ObjectInputStream oistream = new ObjectInputStream(bastream)) { + // return oistream.readObject(); + // } + // } + return Arrays.copyOfRange(buf, offset, offset+len); + + case ParticleType.GEOJSON: + return Buffer.bytesToGeoJSON(buf, offset, len); + + case ParticleType.HLL: + return Buffer.bytesToHLL(buf, offset, len); + + case ParticleType.LIST: + return Unpacker.unpackObjectList(buf, offset, len); + + case ParticleType.MAP: + return Unpacker.unpackObjectMap(buf, offset, len); + + default: + return null; + } + } + + /* + private static Object parseList(byte[] buf, int offset, int len) throws AerospikeException { + int limit = offset + len; + int itemCount = Buffer.bytesToInt(buf, offset); + offset += 4; + ArrayList list = new ArrayList(itemCount); + + while (offset < limit) { + int sz = Buffer.bytesToInt(buf, offset); + offset += 4; + int type = buf[offset]; + offset++; + list.add(bytesToParticle(type, buf, offset, sz)); + offset += sz; + } + return list; + } + + private static Object parseMap(byte[] buf, int offset, int len) throws AerospikeException { + Object key; + Object value; + + int limit = offset + len; + int n_items = Buffer.bytesToInt(buf, offset); + offset += 4; + HashMap map = new HashMap(n_items); + + while (offset < limit) { + // read out the key + int sz = Buffer.bytesToInt(buf, offset); + offset += 4; + int type = buf[offset]; + offset++; + + key = bytesToParticle(type, buf, offset, len); + offset += sz; + + // read out the value + sz = Buffer.bytesToInt(buf, offset); + offset += 4; + type = buf[offset]; + offset++; + + value = bytesToParticle(type, buf, offset, len); + offset += sz; + + map.put(key, value); + } + return map; + } + */ + + /** + * Estimate size of Utf8 encoded bytes without performing the actual encoding. + */ + public static int estimateSizeUtf8(String s) { + if (s == null || s.length() == 0) { + return 0; + } + return Utf8.encodedLength(s); + } + + public static byte[] stringToUtf8(String s) { + if (s == null || s.length() == 0) { + return new byte[0]; + } + int size = Utf8.encodedLength(s); + byte[] bytes = new byte[size]; + stringToUtf8(s, bytes, 0); + return bytes; + } + + /** + * Convert input string to UTF-8, copies into buffer (at given offset). + * Returns number of bytes in the string. + * + * Java's internal UTF8 conversion is very, very slow. + * This is, rather amazingly, 8x faster than the to-string method. + * Returns the number of bytes this translated into. + */ + public static int stringToUtf8(String s, byte[] buf, int offset) { + if (s == null) { + return 0; + } + int length = s.length(); + int startOffset = offset; + + for (int i = 0; i < length; i++) { + int c = s.charAt(i); + if (c < 0x80) { + buf[offset++] = (byte) c; + } + else if (c < 0x800) { + buf[offset++] = (byte)(0xc0 | ((c >> 6))); + buf[offset++] = (byte)(0x80 | (c & 0x3f)); + } + else { + // Encountered a different encoding other than 2-byte UTF8. Let java handle it. + byte[] value = s.getBytes(StandardCharsets.UTF_8); + System.arraycopy(value, 0, buf, startOffset, value.length); + return value.length; + } + } + return offset - startOffset; + } + + public static String utf8ToString(byte[] buf, int offset, int length) { + // A Thread local implementation does not help here, so + // allocate character buffer each time. + if (length == 0) { + return ""; + } + + char[] charBuffer = new char[length]; + int charCount = 0; + int limit = offset + length; + int origoffset = offset; + + while (offset < limit ) { + int b1 = buf[offset]; + + if (b1 >= 0) { + charBuffer[charCount++] = (char)b1; + offset++; + } + else if ((b1 >> 5) == -2) { + int b2 = buf[offset + 1]; + charBuffer[charCount++] = (char) (((b1 << 6) ^ b2) ^ 0x0f80); + offset += 2; + } + else { + // Encountered an UTF encoding which uses more than 2 bytes. + // Use a native function to do the conversion. + return new String(buf, origoffset, length, StandardCharsets.UTF_8); + } + } + return new String(charBuffer, 0, charCount); + } + + public static String utf8ToString(byte[] buf, int offset, int length, StringBuilder sb) { + if (length == 0) { + return ""; + } + + // This method is designed to accommodate multiple string conversions on the same + // thread, but without the ThreadLocal overhead. The StringBuilder instance is + // created on the stack and passed in each method invocation. + sb.setLength(0); + int limit = offset + length; + int origoffset = offset; + + while (offset < limit ) { + if ((buf[offset] & 0x80) == 0) { // 1 byte + char c = (char) buf[offset]; + sb.append(c); + offset++; + } + else if ((buf[offset] & 0xE0) == 0xC0) { // 2 bytes + char c = (char) (((buf[offset] & 0x1f) << 6) | (buf[offset+1] & 0x3f)); + sb.append(c); + offset += 2; + } + else { + // Encountered an UTF encoding which uses more than 2 bytes. + // Use a native function to do the conversion. + return new String(buf, origoffset, length, StandardCharsets.UTF_8); + } + } + return sb.toString(); + } + + /** + * Convert UTF8 numeric digits to an integer. Negative integers are not supported. + * + * Input format: 1234 + */ + public static int utf8DigitsToInt(byte[] buf, int begin, int end) { + int val = 0; + int mult = 1; + + for (int i = end - 1; i >= begin; i--) { + val += (buf[i] - 48) * mult; + mult *= 10; + } + return val; + } + + public static String bytesToHexString(byte[] buf) { + if (buf == null || buf.length == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(buf.length * 2); + + for (int i = 0; i < buf.length; i++) { + sb.append(String.format("%02x", buf[i])); + } + return sb.toString(); + } + + public static String bytesToHexString(byte[] buf, int offset, int length) { + StringBuilder sb = new StringBuilder(length * 2); + + for (int i = offset; i < length; i++) { + sb.append(String.format("%02x", buf[i])); + } + return sb.toString(); + } + + public static Value bytesToLongValue(byte[] buf, int offset, int len) { + long val = 0; + + for (int i = 0; i < len; i++) { + val <<= 8; + val |= buf[offset+i] & 0xFF; + } + + return new Value.LongValue(val); + } + + public static Object bytesToGeoJSON(byte[] buf, int offset, int len) { + // Ignore the flags for now + int ncells = bytesToShort(buf, offset + 1); + int hdrsz = 1 + 2 + (ncells * 8); + return Value.getAsGeoJSON(Buffer.utf8ToString(buf, offset + hdrsz, len - hdrsz)); + } + + public static Object bytesToHLL(byte[] buf, int offset, int len) { + byte[] bytes = Arrays.copyOfRange(buf, offset, offset+len); + return Value.getAsHLL(bytes); + } + + public static Object bytesToNumber(byte[] buf, int offset, int len) { + // Server always returns 8 for integer length. + if (len == 8) { + return bytesToLong(buf, offset); + } + + // Handle other lengths just in case server changes. + if (len < 8) { + // Handle variable length long. + long val = 0; + + for (int i = 0; i < len; i++) { + val <<= 8; + val |= buf[offset+i] & 0xFF; + } + return val; + } + + // Handle huge numbers. + return bytesToBigInteger(buf, offset, len); + } + + public static Object bytesToBigInteger(byte[] buf, int offset, int len) { + boolean negative = false; + + if ((buf[offset] & 0x80) != 0) { + negative = true; + buf[offset] &= 0x7f; + } + byte[] bytes = new byte[len]; + System.arraycopy(buf, offset, bytes, 0, len); + + BigInteger big = new BigInteger(bytes); + + if (negative) { + big = big.negate(); + } + return big; + } + + public static boolean bytesToBool(byte[] buf, int offset, int len) { + if (len <= 0) { + return false; + } + return (buf[offset] == 0)? false : true; + } + + //------------------------------------------------------- + // 64 bit double conversions. + //------------------------------------------------------- + + public static double bytesToDouble(byte[] buf, int offset) { + return Double.longBitsToDouble(bytesToLong(buf, offset)); + } + + public static void doubleToBytes(double v, byte[] buf, int offset) { + Buffer.longToBytes(Double.doubleToLongBits(v), buf, offset); + } + + //------------------------------------------------------- + // 64 bit number conversions. + //------------------------------------------------------- + + /** + * Convert long to big endian signed or unsigned 64 bits. + * The bit pattern will be the same regardless of sign. + */ + public static void longToBytes(long v, byte[] buf, int offset) { + buf[offset++] = (byte)(v >>> 56); + buf[offset++] = (byte)(v >>> 48); + buf[offset++] = (byte)(v >>> 40); + buf[offset++] = (byte)(v >>> 32); + buf[offset++] = (byte)(v >>> 24); + buf[offset++] = (byte)(v >>> 16); + buf[offset++] = (byte)(v >>> 8); + buf[offset] = (byte)(v >>> 0); + } + + /** + * Convert long to little endian signed or unsigned 64 bits. + * The bit pattern will be the same regardless of sign. + */ + public static void longToLittleBytes(long v, byte[] buf, int offset) { + buf[offset++] = (byte)(v >>> 0); + buf[offset++] = (byte)(v >>> 8); + buf[offset++] = (byte)(v >>> 16); + buf[offset++] = (byte)(v >>> 24); + buf[offset++] = (byte)(v >>> 32); + buf[offset++] = (byte)(v >>> 40); + buf[offset++] = (byte)(v >>> 48); + buf[offset] = (byte)(v >>> 56); + } + + /** + * Convert big endian signed 64 bits to long. + */ + public static long bytesToLong(byte[] buf, int offset) { + return ( + ((long)(buf[offset] & 0xFF) << 56) | + ((long)(buf[offset+1] & 0xFF) << 48) | + ((long)(buf[offset+2] & 0xFF) << 40) | + ((long)(buf[offset+3] & 0xFF) << 32) | + ((long)(buf[offset+4] & 0xFF) << 24) | + ((long)(buf[offset+5] & 0xFF) << 16) | + ((long)(buf[offset+6] & 0xFF) << 8) | + ((long)(buf[offset+7] & 0xFF) << 0) + ); + } + + /** + * Convert little endian signed 64 bits to long. + */ + public static long littleBytesToLong(byte[] buf, int offset) { + return ( + ((long)(buf[offset] & 0xFF) << 0) | + ((long)(buf[offset+1] & 0xFF) << 8) | + ((long)(buf[offset+2] & 0xFF) << 16) | + ((long)(buf[offset+3] & 0xFF) << 24) | + ((long)(buf[offset+4] & 0xFF) << 32) | + ((long)(buf[offset+5] & 0xFF) << 40) | + ((long)(buf[offset+6] & 0xFF) << 48) | + ((long)(buf[offset+7] & 0xFF) << 56) + ); + } + + //------------------------------------------------------- + // Transaction version conversions. + //------------------------------------------------------- + + /** + * Convert long to a 7 byte record version for transaction. + */ + public static void longToVersionBytes(long v, byte[] buf, int offset) { + buf[offset++] = (byte)(v >>> 0); + buf[offset++] = (byte)(v >>> 8); + buf[offset++] = (byte)(v >>> 16); + buf[offset++] = (byte)(v >>> 24); + buf[offset++] = (byte)(v >>> 32); + buf[offset++] = (byte)(v >>> 40); + buf[offset] = (byte)(v >>> 48); + } + + /** + * Convert 7 byte record version to a long for transaction. + */ + public static long versionBytesToLong(byte[] buf, int offset) { + return ( + ((long)(buf[offset] & 0xFF) << 0) | + ((long)(buf[offset+1] & 0xFF) << 8) | + ((long)(buf[offset+2] & 0xFF) << 16) | + ((long)(buf[offset+3] & 0xFF) << 24) | + ((long)(buf[offset+4] & 0xFF) << 32) | + ((long)(buf[offset+5] & 0xFF) << 40) | + ((long)(buf[offset+6] & 0xFF) << 48) + ); + } + + //------------------------------------------------------- + // 32 bit number conversions. + //------------------------------------------------------- + + /** + * Convert int to big endian signed or unsigned 32 bits. + * The bit pattern will be the same regardless of sign. + */ + public static void intToBytes(int v, byte[] buf, int offset) { + buf[offset++] = (byte)(v >>> 24); + buf[offset++] = (byte)(v >>> 16); + buf[offset++] = (byte)(v >>> 8); + buf[offset] = (byte)(v >>> 0); + } + + /** + * Convert int to little endian signed or unsigned 32 bits. + * The bit pattern will be the same regardless of sign. + */ + public static void intToLittleBytes(int v, byte[] buf, int offset) { + buf[offset++] = (byte)(v >>> 0); + buf[offset++] = (byte)(v >>> 8); + buf[offset++] = (byte)(v >>> 16); + buf[offset] = (byte)(v >>> 24); + } + + /** + * Convert big endian signed 32 bits to int. + */ + public static int bytesToInt(byte[] buf, int offset) { + return ( + ((buf[offset] & 0xFF) << 24) | + ((buf[offset+1] & 0xFF) << 16) | + ((buf[offset+2] & 0xFF) << 8) | + ((buf[offset+3] & 0xFF) << 0) + ); + } + + /** + * Convert little endian signed 32 bits to int. + */ + public static int littleBytesToInt(byte[] buf, int offset) { + return ( + ((buf[offset] & 0xFF) << 0) | + ((buf[offset+1] & 0xFF) << 8) | + ((buf[offset+2] & 0xFF) << 16) | + ((buf[offset+3] & 0xFF) << 24) + ); + } + + /** + * Convert big endian unsigned 32 bits to long. + */ + public static long bigUnsigned32ToLong(byte[] buf, int offset) { + return ( + ((long)(buf[offset] & 0xFF) << 24) | + ((long)(buf[offset+1] & 0xFF) << 16) | + ((long)(buf[offset+2] & 0xFF) << 8) | + ((long)(buf[offset+3] & 0xFF) << 0) + ); + } + + //------------------------------------------------------- + // 16 bit number conversions. + //------------------------------------------------------- + + /** + * Convert int to big endian signed or unsigned 16 bits. + * The bit pattern will be the same regardless of sign. + */ + public static void shortToBytes(int v, byte[] buf, int offset) { + buf[offset++] = (byte)(v >>> 8); + buf[offset] = (byte)(v >>> 0); + } + + /** + * Convert int to little endian signed or unsigned 16 bits. + * The bit pattern will be the same regardless of sign. + */ + public static void shortToLittleBytes(int v, byte[] buf, int offset) { + buf[offset++] = (byte)(v >>> 0); + buf[offset] = (byte)(v >>> 8); + } + + /** + * Convert big endian unsigned 16 bits to int. + */ + public static int bytesToShort(byte[] buf, int offset) { + return ( + ((buf[offset] & 0xFF) << 8) | + ((buf[offset+1] & 0xFF) << 0) + ); + } + + /** + * Convert little endian unsigned 16 bits to int. + */ + public static int littleBytesToShort(byte[] buf, int offset) { + return ( + ((buf[offset] & 0xFF) << 0) | + ((buf[offset+1] & 0xFF) << 8) + ); + } + + /** + * Convert big endian signed 16 bits to short. + */ + public static short bigSigned16ToShort(byte[] buf, int offset) { + return (short)( + ((buf[offset] & 0xFF) << 8) | + ((buf[offset+1] & 0xFF) << 0) + ); + } + + //------------------------------------------------------- + // Variable byte number conversions. + //------------------------------------------------------- + + /** + * Encode an integer in variable 7-bit format. + * The high bit indicates if more bytes are used. + * Return byte size of integer. + */ + public static int intToVarBytes(int v, byte[] buf, int offset) { + int i = offset; + + while (i < buf.length && v >= 0x80) { + buf[i++] = (byte)(v | 0x80); + v >>>= 7; + } + + if (i < buf.length) { + buf[i++] = (byte)v; + return i - offset; + } + return 0; + } + + /** + * Decode an integer in variable 7-bit format. + * The high bit indicates if more bytes are used. + * Return value and byte size in array. + */ + public static int[] varBytesToInt(byte[] buf, int offset) { + int i = offset; + int val = 0; + int shift = 0; + byte b; + + do { + b = buf[i++]; + val |= (b & 0x7F) << shift; + shift += 7; + } while ((b & 0x80) != 0); + + return new int[] {val, i - offset}; + } +} diff --git a/src/main/java/com/aerospike/dsl/client/command/Command.java b/src/main/java/com/aerospike/dsl/client/command/Command.java new file mode 100644 index 0000000..6882533 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/command/Command.java @@ -0,0 +1,109 @@ +/* + * Copyright 2012-2025 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.command; + +public class Command { + public static final int INFO1_READ = (1 << 0); // Contains a read operation. + public static final int INFO1_GET_ALL = (1 << 1); // Get all bins. + public static final int INFO1_SHORT_QUERY = (1 << 2); // Short query. + public static final int INFO1_BATCH = (1 << 3); // Batch read or exists. + public static final int INFO1_XDR = (1 << 4); // Operation is being performed by XDR. + public static final int INFO1_NOBINDATA = (1 << 5); // Do not read the bins. + public static final int INFO1_READ_MODE_AP_ALL = (1 << 6); // Involve all replicas in read operation. + public static final int INFO1_COMPRESS_RESPONSE = (1 << 7); // Tell server to compress it's response. + + public static final int INFO2_WRITE = (1 << 0); // Create or update record + public static final int INFO2_DELETE = (1 << 1); // Fling a record into the belly of Moloch. + public static final int INFO2_GENERATION = (1 << 2); // Update if expected generation == old. + public static final int INFO2_GENERATION_GT = (1 << 3); // Update if new generation >= old, good for restore. + public static final int INFO2_DURABLE_DELETE = (1 << 4); // Command resulting in record deletion leaves tombstone (Enterprise only). + public static final int INFO2_CREATE_ONLY = (1 << 5); // Create only. Fail if record already exists. + public static final int INFO2_RELAX_AP_LONG_QUERY = (1 << 6); // Treat as long query, but relax read consistency. + public static final int INFO2_RESPOND_ALL_OPS = (1 << 7); // Return a result for every operation. + + public static final int INFO3_LAST = (1 << 0); // This is the last of a multi-part message. + public static final int INFO3_COMMIT_MASTER = (1 << 1); // Commit to master only before declaring success. + // On send: Do not return partition done in scan/query. + // On receive: Specified partition is done in scan/query. + public static final int INFO3_PARTITION_DONE = (1 << 2); + public static final int INFO3_UPDATE_ONLY = (1 << 3); // Update only. Merge bins. + public static final int INFO3_CREATE_OR_REPLACE = (1 << 4); // Create or completely replace record. + public static final int INFO3_REPLACE_ONLY = (1 << 5); // Completely replace existing record only. + public static final int INFO3_SC_READ_TYPE = (1 << 6); // See below. + public static final int INFO3_SC_READ_RELAX = (1 << 7); // See below. + + // Interpret SC_READ bits in info3. + // + // RELAX TYPE + // strict + // ------ + // 0 0 sequential (default) + // 0 1 linearize + // + // relaxed + // ------- + // 1 0 allow replica + // 1 1 allow unavailable + + public static final int INFO4_TXN_VERIFY_READ = (1 << 0); // Send transaction version to the server to be verified. + public static final int INFO4_TXN_ROLL_FORWARD = (1 << 1); // Roll forward transaction. + public static final int INFO4_TXN_ROLL_BACK = (1 << 2); // Roll back transaction. + public static final int INFO4_TXN_ON_LOCKING_ONLY = (1 << 4); // Must be able to lock record in transaction. + + public static final byte STATE_READ_AUTH_HEADER = 1; + public static final byte STATE_READ_HEADER = 2; + public static final byte STATE_READ_DETAIL = 3; + public static final byte STATE_COMPLETE = 4; + + public static final byte BATCH_MSG_READ = 0x0; + public static final byte BATCH_MSG_REPEAT = 0x1; + public static final byte BATCH_MSG_INFO = 0x2; + public static final byte BATCH_MSG_GEN = 0x4; + public static final byte BATCH_MSG_TTL = 0x8; + public static final byte BATCH_MSG_INFO4 = 0x10; + + public static final int MSG_TOTAL_HEADER_SIZE = 30; + public static final int FIELD_HEADER_SIZE = 5; + public static final int OPERATION_HEADER_SIZE = 8; + public static final int MSG_REMAINING_HEADER_SIZE = 22; + public static final int COMPRESS_THRESHOLD = 128; + public static final long CL_MSG_VERSION = 2L; + public static final long AS_MSG_TYPE = 3L; + public static final long MSG_TYPE_COMPRESSED = 4L; + + public byte[] dataBuffer; + public int dataOffset; + public final int maxRetries; + public final int serverTimeout; + public int socketTimeout; + public int totalTimeout; + public Long version; + + public Command(int socketTimeout, int totalTimeout, int maxRetries) { + this.maxRetries = maxRetries; + this.totalTimeout = totalTimeout; + + if (totalTimeout > 0) { + this.socketTimeout = (socketTimeout < totalTimeout && socketTimeout > 0)? socketTimeout : totalTimeout; + this.serverTimeout = this.socketTimeout; + } + else { + this.socketTimeout = socketTimeout; + this.serverTimeout = 0; + } + } +} diff --git a/src/main/java/com/aerospike/dsl/client/command/ParticleType.java b/src/main/java/com/aerospike/dsl/client/command/ParticleType.java new file mode 100644 index 0000000..13122ba --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/command/ParticleType.java @@ -0,0 +1,34 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.command; + +/** + * Server particle types. + */ +public final class ParticleType { + public static final int NULL = 0; + public static final int INTEGER = 1; + public static final int DOUBLE = 2; + public static final int STRING = 3; + public static final int BLOB = 4; + public static final int JBLOB = 7; + public static final int BOOL = 17; + public static final int HLL = 18; + public static final int MAP = 19; + public static final int LIST = 20; + public static final int GEOJSON = 23; +} diff --git a/src/main/java/com/aerospike/dsl/client/exp/Exp.java b/src/main/java/com/aerospike/dsl/client/exp/Exp.java new file mode 100644 index 0000000..4bf6a5d --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/exp/Exp.java @@ -0,0 +1,1555 @@ +/* + * Copyright 2012-2024 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.exp; + +import com.aerospike.dsl.client.command.ParticleType; +import com.aerospike.dsl.client.query.RegexFlag; +import com.aerospike.dsl.client.util.Packer; + +import java.util.Calendar; +import java.util.List; +import java.util.Map; + +/** + * Expression generator. + */ +public abstract class Exp { + /** + * Expression type. + */ + public enum Type { + NIL(0), + BOOL(1), + INT(2), + STRING(3), + LIST(4), + MAP(5), + BLOB(6), + FLOAT(7), + GEO(8), + HLL(9); + + public final int code; + + Type(int code) { + this.code = code; + } + } + + //-------------------------------------------------- + // Build + //-------------------------------------------------- + + /** + * Create final expression that contains packed byte instructions used in the wire protocol. + */ + public static Expression build(Exp exp) { + return new Expression(exp); + } + + //-------------------------------------------------- + // Record Key + //-------------------------------------------------- + + /** + * Create record key expression of specified type. + * + *
{@code
+     * // Integer record key >= 100000
+     * Exp.ge(Exp.key(Type.INT), Exp.val(100000))
+     * }
+ */ + public static Exp key(Type type) { + return new CmdInt(KEY, type.code); + } + + /** + * Create expression that returns if the primary key is stored in the record meta data + * as a boolean expression. + * + *
{@code
+     * // Key exists in record meta data
+     * Exp.keyExists()
+     * }
+ */ + public static Exp keyExists() { + return new Cmd(KEY_EXISTS); + } + + //-------------------------------------------------- + // Record Bin + //-------------------------------------------------- + + /** + * Create bin expression of specified type. + * + *
{@code
+     * // String bin "a" == "views"
+     * Exp.eq(Exp.bin("a", Type.STRING), Exp.val("views"))
+     * }
+ */ + public static Exp bin(String name, Type type) { + return new Bin(name, type); + } + + /** + * Create 64 bit integer bin expression. + * + *
{@code
+     * // Integer bin "a" == 200
+     * Exp.eq(Exp.intBin("a"), Exp.val(200))
+     * }
+ */ + public static Exp intBin(String name) { + return new Bin(name, Type.INT); + } + + /** + * Create 64 bit float bin expression. + * + *
{@code
+     * // Float bin "a" >= 1.5
+     * Exp.ge(Exp.floatBin("a"), Exp.val(1.5))
+     * }
+ */ + public static Exp floatBin(String name) { + return new Bin(name, Type.FLOAT); + } + + /** + * Create string bin expression. + * + *
{@code
+     * // String bin "a" == "views"
+     * Exp.eq(Exp.stringBin("a"), Exp.val("views"))
+     * }
+ */ + public static Exp stringBin(String name) { + return new Bin(name, Type.STRING); + } + + /** + * Create boolean bin expression. + * + *
{@code
+     * // Boolean bin "a" == true
+     * Exp.eq(Exp.boolBin("a"), Exp.val(true))
+     * }
+ */ + public static Exp boolBin(String name) { + return new Bin(name, Type.BOOL); + } + + /** + * Create byte[] bin expression. + * + *
{@code
+     * // Blob bin "a" == [1,2,3]
+     * Exp.eq(Exp.blobBin("a"), Exp.val(new byte[] {1, 2, 3}))
+     * }
+ */ + public static Exp blobBin(String name) { + return new Bin(name, Type.BLOB); + } + + /** + * Create geospatial bin expression. + * + *
{@code
+     * // Geo bin "a" == region
+     * String region = "{ \"type\": \"AeroCircle\", \"coordinates\": [[-122.0, 37.5], 50000.0] }";
+     * Exp.geoCompare(Exp.geoBin("loc"), Exp.geo(region))
+     * }
+ */ + public static Exp geoBin(String name) { + return new Bin(name, Type.GEO); + } + + /** + * Create list bin expression. + * + *
{@code
+     * // Bin a[2] == 3
+     * Exp.eq(ListExp.getByIndex(ListReturnType.VALUE, Type.INT, Exp.val(2), Exp.listBin("a")), Exp.val(3))
+     * }
+ */ + public static Exp listBin(String name) { + return new Bin(name, Type.LIST); + } + + /** + * Create map bin expression. + * + *
{@code
+     * // Bin a["key"] == "value"
+     * Exp.eq(
+     *     MapExp.getByKey(MapReturnType.VALUE, Type.STRING, Exp.val("key"), Exp.mapBin("a")),
+     *     Exp.val("value"));
+     * }
+ */ + public static Exp mapBin(String name) { + return new Bin(name, Type.MAP); + } + + /** + * Create hll bin expression. + * + *
{@code
+     * // HLL bin "a" count > 7
+     * Exp.gt(HLLExp.getCount(Exp.hllBin("a")), Exp.val(7))
+     * }
+ */ + public static Exp hllBin(String name) { + return new Bin(name, Type.HLL); + } + + /** + * Create expression that returns if bin of specified name exists. + * + *
{@code
+     * // Bin "a" exists in record
+     * Exp.binExists("a")
+     * }
+ */ + public static Exp binExists(String name) { + return Exp.ne(Exp.binType(name), Exp.val(0)); + } + + /** + * Create expression that returns bin's integer particle type. + * See {@link ParticleType}. + * + *
{@code
+     * // Bin "a" particle type is a list
+     * Exp.eq(Exp.binType("a"), Exp.val(ParticleType.LIST))
+     * }
+ */ + public static Exp binType(String name) { + return new CmdStr(BIN_TYPE, name); + } + + //-------------------------------------------------- + // Misc + //-------------------------------------------------- + + /** + * Create expression that returns record set name string. This expression usually + * evaluates quickly because record meta data is cached in memory. + * + *
{@code
+     * // Record set name == "myset"
+     * Exp.eq(Exp.setName(), Exp.val("myset"))
+     * }
+ */ + public static Exp setName() { + return new Cmd(SET_NAME); + } + + /** + * Create expression that returns the record size. This expression usually evaluates + * quickly because record meta data is cached in memory. + *

+ * Requires server version 7.0+. This expression replaces {@link #deviceSize()} and + * {@link #memorySize()} since those older expressions are equivalent on server version 7.0+. + * + *

{@code
+     * // Record size >= 100 KB
+     * Exp.ge(Exp.recordSize(), Exp.val(100 * 1024))
+     * }
+ */ + public static Exp recordSize() { + return new Cmd(RECORD_SIZE); + } + + /** + * Create expression that returns record size on disk. If server storage-engine is + * memory, then zero is returned. This expression usually evaluates quickly because + * record meta data is cached in memory. + *

+ * This expression should only be used for server versions less than 7.0. Use + * {@link #recordSize()} for server version 7.0+. + * + *

{@code
+     * // Record device size >= 100 KB
+     * Exp.ge(Exp.recordSize(), Exp.val(100 * 1024))
+     * }
+ */ + @Deprecated + public static Exp deviceSize() { + return new Cmd(DEVICE_SIZE); + } + + /** + * Create expression that returns record size in memory. If server storage-engine is + * not memory nor data-in-memory, then zero is returned. This expression usually evaluates + * quickly because record meta data is cached in memory. + *

+ * Requires server version between 5.3 inclusive and 7.0 exclusive. + * Use {@link #recordSize()} for server version 7.0+. + * + *

{@code
+     * // Record memory size >= 100 KB
+     * Exp.ge(Exp.memorySize(), Exp.val(100 * 1024))
+     * }
+ */ + @Deprecated + public static Exp memorySize() { + return new Cmd(MEMORY_SIZE); + } + + /** + * Create expression that returns record last update time expressed as 64 bit integer + * nanoseconds since 1970-01-01 epoch. This expression usually evaluates quickly because + * record meta data is cached in memory. + * + *
{@code
+     * // Record last update time >= 2020-01-15
+     * Exp.ge(Exp.lastUpdate(), Exp.val(new GregorianCalendar(2020, 0, 15)))
+     * }
+ */ + public static Exp lastUpdate() { + return new Cmd(LAST_UPDATE); + } + + /** + * Create expression that returns milliseconds since the record was last updated. + * This expression usually evaluates quickly because record meta data is cached in memory. + * + *
{@code
+     * // Record last updated more than 2 hours ago
+     * Exp.gt(Exp.sinceUpdate(), Exp.val(2 * 60 * 60 * 1000))
+     * }
+ */ + public static Exp sinceUpdate() { + return new Cmd(SINCE_UPDATE); + } + + /** + * Create expression that returns record expiration time expressed as 64 bit integer + * nanoseconds since 1970-01-01 epoch. This expression usually evaluates quickly because + * record meta data is cached in memory. + * + *
{@code
+     * // Record expires on 2021-01-01
+     * Exp.and(
+     *   Exp.ge(Exp.voidTime(), Exp.val(new GregorianCalendar(2021, 0, 1))),
+     *   Exp.lt(Exp.voidTime(), Exp.val(new GregorianCalendar(2021, 0, 2))))
+     * }
+ */ + public static Exp voidTime() { + return new Cmd(VOID_TIME); + } + + /** + * Create expression that returns record expiration time (time to live) in integer seconds. + * This expression usually evaluates quickly because record meta data is cached in memory. + * + *
{@code
+     * // Record expires in less than 1 hour
+     * Exp.lt(Exp.ttl(), Exp.val(60 * 60))
+     * }
+ */ + public static Exp ttl() { + return new Cmd(TTL); + } + + /** + * Create expression that returns if record has been deleted and is still in tombstone state. + * This expression usually evaluates quickly because record meta data is cached in memory. + * + *
{@code
+     * // Deleted records that are in tombstone state.
+     * Exp.isTombstone()
+     * }
+ */ + public static Exp isTombstone() { + return new Cmd(IS_TOMBSTONE); + } + + /** + * Create expression that returns record digest modulo as integer. This expression usually + * evaluates quickly because record meta data is cached in memory. + * + *
{@code
+     * // Records that have digest(key) % 3 == 1
+     * Exp.eq(Exp.digestModulo(3), Exp.val(1))
+     * }
+ */ + public static Exp digestModulo(int mod) { + return new CmdInt(DIGEST_MODULO, mod); + } + + /** + * Create expression that performs a regex match on a string bin or string value expression. + * + *
{@code
+     * // Select string bin "a" that starts with "prefix" and ends with "suffix".
+     * // Ignore case and do not match newline.
+     * Exp.regexCompare("prefix.*suffix", RegexFlag.ICASE | RegexFlag.NEWLINE, Exp.stringBin("a"))
+     * }
+ * + * @param regex regular expression string + * @param flags regular expression bit flags. See {@link RegexFlag} + * @param bin string bin or string value expression + */ + public static Exp regexCompare(String regex, int flags, Exp bin) { + return new Regex(bin, regex, flags); + } + + //-------------------------------------------------- + // GEO Spatial + //-------------------------------------------------- + + /** + * Create compare geospatial operation. + * + *
{@code
+     * // Query region within coordinates.
+     * String region =
+     * "{ " +
+     * "  \"type\": \"Polygon\", " +
+     * "  \"coordinates\": [ " +
+     * "    [[-122.500000, 37.000000],[-121.000000, 37.000000], " +
+     * "     [-121.000000, 38.080000],[-122.500000, 38.080000], " +
+     * "     [-122.500000, 37.000000]] " +
+     * "    ] " +
+     * "}";
+     * Exp.geoCompare(Exp.geoBin("a"), Exp.geo(region))
+     * }
+ */ + public static Exp geoCompare(Exp left, Exp right) { + return new CmdExp(GEO, left, right); + } + + /** + * Create geospatial json string value. + */ + public static Exp geo(String val) { + return new Geo(val); + } + + //-------------------------------------------------- + // Value + //-------------------------------------------------- + + /** + * Create boolean value. + */ + public static Exp val(boolean val) { + return new Bool(val); + } + + /** + * Create 64 bit integer value. + */ + public static Exp val(long val) { + return new Int(val); + } + + /** + * Create Calendar value expressed in nanoseconds since 1970-01-01 epoch as 64 bit integer. + */ + public static Exp val(Calendar val) { + return new Int(val.getTimeInMillis() * NANOS_PER_MILLIS); + } + + /** + * Create 64 bit floating point value. + */ + public static Exp val(double val) { + return new Float(val); + } + + /** + * Create string value. + */ + public static Exp val(String val) { + return new Str(val); + } + + /** + * Create blob byte[] value. + */ + public static Exp val(byte[] val) { + return new Blob(val); + } + + /** + * Create list value. + */ + public static Exp val(List list) { + return new ListVal(list); + } + + /** + * Create map value. For ordered maps, pass in a TreeMap or a map that implements the SortedMap + * interface. For unordered maps, pass in a HashMap. + */ + public static Exp val(Map map) { + return new MapVal(map); + } + + /** + * Create nil value. + */ + public static Exp nil() { + return new Nil(); + } + + /** + * Create infinity value for use in CDT range expressions. + */ + public static Exp inf() { + return new Infinity(); + } + + /** + * Create wildcard value for use in CDT expressions. + */ + public static Exp wildcard() { + return new Wildcard(); + } + + //-------------------------------------------------- + // Boolean Operator + //-------------------------------------------------- + + /** + * Create "not" operator expression. + * + *
{@code
+     * // ! (a == 0 || a == 10)
+     * Exp.not(
+     *   Exp.or(
+     *     Exp.eq(Exp.intBin("a"), Exp.val(0)),
+     *     Exp.eq(Exp.intBin("a"), Exp.val(10))))
+     * }
+ */ + public static Exp not(Exp exp) { + return new CmdExp(NOT, exp); + } + + /** + * Create "and" (&&) operator that applies to a variable number of expressions. + * + *
{@code
+     * // (a > 5 || a == 0) && b < 3
+     * Exp.and(
+     *   Exp.or(
+     *     Exp.gt(Exp.intBin("a"), Exp.val(5)),
+     *     Exp.eq(Exp.intBin("a"), Exp.val(0))),
+     *   Exp.lt(Exp.intBin("b"), Exp.val(3)))
+     * }
+ */ + public static Exp and(Exp... exps) { + return new CmdExp(AND, exps); + } + + /** + * Create "or" (||) operator that applies to a variable number of expressions. + * + *
{@code
+     * // a == 0 || b == 0
+     * Exp.or(
+     *   Exp.eq(Exp.intBin("a"), Exp.val(0)),
+     *   Exp.eq(Exp.intBin("b"), Exp.val(0)));
+     * }
+ */ + public static Exp or(Exp... exps) { + return new CmdExp(OR, exps); + } + + /** + * Create expression that returns true if only one of the expressions are true. + * Requires server version 5.6.0+. + * + *
{@code
+     * // exclusive(a == 0, b == 0)
+     * Exp.exclusive(
+     *   Exp.eq(Exp.intBin("a"), Exp.val(0)),
+     *   Exp.eq(Exp.intBin("b"), Exp.val(0)));
+     * }
+ */ + public static Exp exclusive(Exp... exps) { + return new CmdExp(EXCLUSIVE, exps); + } + + /** + * Create equal (==) expression. + * + *
{@code
+     * // a == 11
+     * Exp.eq(Exp.intBin("a"), Exp.val(11))
+     * }
+ */ + public static Exp eq(Exp left, Exp right) { + return new CmdExp(EQ, left, right); + } + + /** + * Create not equal (!=) expression + * + *
{@code
+     * // a != 13
+     * Exp.ne(Exp.intBin("a"), Exp.val(13))
+     * }
+ */ + public static Exp ne(Exp left, Exp right) { + return new CmdExp(NE, left, right); + } + + /** + * Create greater than (>) operation. + * + *
{@code
+     * // a > 8
+     * Exp.gt(Exp.intBin("a"), Exp.val(8))
+     * }
+ */ + public static Exp gt(Exp left, Exp right) { + return new CmdExp(GT, left, right); + } + + /** + * Create greater than or equal (>=) operation. + * + *
{@code
+     * // a >= 88
+     * Exp.ge(Exp.intBin("a"), Exp.val(88))
+     * }
+ */ + public static Exp ge(Exp left, Exp right) { + return new CmdExp(GE, left, right); + } + + /** + * Create less than (<) operation. + * + *
{@code
+     * // a < 1000
+     * Exp.lt(Exp.intBin("a"), Exp.val(1000))
+     * }
+ */ + public static Exp lt(Exp left, Exp right) { + return new CmdExp(LT, left, right); + } + + /** + * Create less than or equals (<=) operation. + * + *
{@code
+     * // a <= 1
+     * Exp.le(Exp.intBin("a"), Exp.val(1))
+     * }
+ */ + public static Exp le(Exp left, Exp right) { + return new CmdExp(LE, left, right); + } + + //-------------------------------------------------- + // Number Operator + //-------------------------------------------------- + + /** + * Create "add" (+) operator that applies to a variable number of expressions. + * Return sum of all arguments. All arguments must resolve to the same type (integer or float). + * Requires server version 5.6.0+. + * + *
{@code
+     * // a + b + c == 10
+     * Exp.eq(
+     *   Exp.add(Exp.intBin("a"), Exp.intBin("b"), Exp.intBin("c")),
+     *   Exp.val(10));
+     * }
+ */ + public static Exp add(Exp... exps) { + return new CmdExp(ADD, exps); + } + + /** + * Create "subtract" (-) operator that applies to a variable number of expressions. + * If only one argument is provided, return the negation of that argument. + * Otherwise, return the sum of the 2nd to Nth argument subtracted from the 1st + * argument. All arguments must resolve to the same type (integer or float). + * Requires server version 5.6.0+. + * + *
{@code
+     * // a - b - c > 10
+     * Exp.gt(
+     *   Exp.sub(Exp.intBin("a"), Exp.intBin("b"), Exp.intBin("c")),
+     *   Exp.val(10));
+     * }
+ */ + public static Exp sub(Exp... exps) { + return new CmdExp(SUB, exps); + } + + /** + * Create "multiply" (*) operator that applies to a variable number of expressions. + * Return the product of all arguments. If only one argument is supplied, return + * that argument. All arguments must resolve to the same type (integer or float). + * Requires server version 5.6.0+. + * + *
{@code
+     * // a * b * c < 100
+     * Exp.lt(
+     *   Exp.mul(Exp.intBin("a"), Exp.intBin("b"), Exp.intBin("c")),
+     *   Exp.val(100));
+     * }
+ */ + public static Exp mul(Exp... exps) { + return new CmdExp(MUL, exps); + } + + /** + * Create "divide" (/) operator that applies to a variable number of expressions. + * If there is only one argument, returns the reciprocal for that argument. + * Otherwise, return the first argument divided by the product of the rest. + * All arguments must resolve to the same type (integer or float). + * Requires server version 5.6.0+. + * + *
{@code
+     * // a / b / c > 1
+     * Exp.gt(
+     *   Exp.div(Exp.intBin("a"), Exp.intBin("b"), Exp.intBin("c")),
+     *   Exp.val(1));
+     * }
+ */ + public static Exp div(Exp... exps) { + return new CmdExp(DIV, exps); + } + + /** + * Create "power" operator that raises a "base" to the "exponent" power. + * All arguments must resolve to floats. + * Requires server version 5.6.0+. + * + *
{@code
+     * // pow(a, 2.0) == 4.0
+     * Exp.eq(
+     *   Exp.pow(Exp.floatBin("a"), Exp.val(2.0)),
+     *   Exp.val(4.0));
+     * }
+ */ + public static Exp pow(Exp base, Exp exponent) { + return new CmdExp(POW, base, exponent); + } + + /** + * Create "log" operator for logarithm of "num" with base "base". + * All arguments must resolve to floats. + * Requires server version 5.6.0+. + * + *
{@code
+     * // log(a, 2.0) == 4.0
+     * Exp.eq(
+     *   Exp.log(Exp.floatBin("a"), Exp.val(2.0)),
+     *   Exp.val(4.0));
+     * }
+ */ + public static Exp log(Exp num, Exp base) { + return new CmdExp(LOG, num, base); + } + + /** + * Create "modulo" (%) operator that determines the remainder of "numerator" + * divided by "denominator". All arguments must resolve to integers. + * Requires server version 5.6.0+. + * + *
{@code
+     * // a % 10 == 0
+     * Exp.eq(
+     *   Exp.mod(Exp.intBin("a"), Exp.val(10)),
+     *   Exp.val(0));
+     * }
+ */ + public static Exp mod(Exp numerator, Exp denominator) { + return new CmdExp(MOD, numerator, denominator); + } + + /** + * Create operator that returns absolute value of a number. + * All arguments must resolve to integer or float. + * Requires server version 5.6.0+. + * + *
{@code
+     * // abs(a) == 1
+     * Exp.eq(
+     *   Exp.abs(Exp.intBin("a")),
+     *   Exp.val(1));
+     * }
+ */ + public static Exp abs(Exp value) { + return new CmdExp(ABS, value); + } + + /** + * Create expression that rounds a floating point number down to the closest integer value. + * The return type is float. Requires server version 5.6.0+. + * + *
{@code
+     * // floor(2.95) == 2.0
+     * Exp.eq(
+     *   Exp.floor(Exp.val(2.95)),
+     *   Exp.val(2.0));
+     * }
+ */ + public static Exp floor(Exp num) { + return new CmdExp(FLOOR, num); + } + + /** + * Create expression that rounds a floating point number up to the closest integer value. + * The return type is float. Requires server version 5.6.0+. + * + *
{@code
+     * // ceil(2.15) >= 3.0
+     * Exp.ge(
+     *   Exp.ceil(Exp.val(2.15)),
+     *   Exp.val(3.0));
+     * }
+ */ + public static Exp ceil(Exp num) { + return new CmdExp(CEIL, num); + } + + /** + * Create expression that converts a float to an integer. + * Requires server version 5.6.0+. + * + *
{@code
+     * // int(2.5) == 2
+     * Exp.eq(
+     *   Exp.toInt(Exp.val(2.5)),
+     *   Exp.val(2));
+     * }
+ */ + public static Exp toInt(Exp num) { + return new CmdExp(TO_INT, num); + } + + /** + * Create expression that converts an integer to a float. + * Requires server version 5.6.0+. + * + *
{@code
+     * // float(2) == 2.0
+     * Exp.eq(
+     *   Exp.toFloat(Exp.val(2))),
+     *   Exp.val(2.0));
+     * }
+ */ + public static Exp toFloat(Exp num) { + return new CmdExp(TO_FLOAT, num); + } + + /** + * Create integer "and" (&) operator that is applied to two or more integers. + * All arguments must resolve to integers. + * Requires server version 5.6.0+. + * + *
{@code
+     * // a & 0xff == 0x11
+     * Exp.eq(
+     *   Exp.intAnd(Exp.intBin("a"), Exp.val(0xff)),
+     *   Exp.val(0x11));
+     * }
+ */ + public static Exp intAnd(Exp... exps) { + return new CmdExp(INT_AND, exps); + } + + /** + * Create integer "or" (|) operator that is applied to two or more integers. + * All arguments must resolve to integers. + * Requires server version 5.6.0+. + * + *
{@code
+     * // a | 0x10 != 0
+     * Exp.ne(
+     *   Exp.intOr(Exp.intBin("a"), Exp.val(0x10)),
+     *   Exp.val(0));
+     * }
+ */ + public static Exp intOr(Exp... exps) { + return new CmdExp(INT_OR, exps); + } + + /** + * Create integer "xor" (^) operator that is applied to two or more integers. + * All arguments must resolve to integers. + * Requires server version 5.6.0+. + * + *
{@code
+     * // a ^ b == 16
+     * Exp.eq(
+     *   Exp.intXor(Exp.intBin("a"), Exp.intBin("b")),
+     *   Exp.val(16));
+     * }
+ */ + public static Exp intXor(Exp... exps) { + return new CmdExp(INT_XOR, exps); + } + + /** + * Create integer "not" (~) operator. + * Requires server version 5.6.0+. + * + *
{@code
+     * // ~a == 7
+     * Exp.eq(
+     *   Exp.intNot(Exp.intBin("a")),
+     *   Exp.val(7));
+     * }
+ */ + public static Exp intNot(Exp exp) { + return new CmdExp(INT_NOT, exp); + } + + /** + * Create integer "left shift" (<<) operator. + * Requires server version 5.6.0+. + * + *
{@code
+     * // a << 8 > 0xff
+     * Exp.gt(
+     *   Exp.lshift(Exp.intBin("a"), Exp.val(8)),
+     *   Exp.val(0xff));
+     * }
+ */ + public static Exp lshift(Exp value, Exp shift) { + return new CmdExp(INT_LSHIFT, value, shift); + } + + /** + * Create integer "logical right shift" (>>>) operator. + * Requires server version 5.6.0+. + * + *
{@code
+     * // a >>> 8 > 0xff
+     * Exp.gt(
+     *   Exp.rshift(Exp.intBin("a"), Exp.val(8)),
+     *   Exp.val(0xff));
+     * }
+ */ + public static Exp rshift(Exp value, Exp shift) { + return new CmdExp(INT_RSHIFT, value, shift); + } + + /** + * Create integer "arithmetic right shift" (>>) operator. + * Requires server version 5.6.0+. + * + *
{@code
+     * // a >> 8 > 0xff
+     * Exp.gt(
+     *   Exp.arshift(Exp.intBin("a"), Exp.val(8)),
+     *   Exp.val(0xff));
+     * }
+ */ + public static Exp arshift(Exp value, Exp shift) { + return new CmdExp(INT_ARSHIFT, value, shift); + } + + /** + * Create expression that returns count of integer bits that are set to 1. + * Requires server version 5.6.0+. + * + *
{@code
+     * // count(a) == 4
+     * Exp.eq(
+     *   Exp.count(Exp.intBin("a")),
+     *   Exp.val(4));
+     * }
+ */ + public static Exp count(Exp exp) { + return new CmdExp(INT_COUNT, exp); + } + + /** + * Create expression that scans integer bits from left (most significant bit) to + * right (least significant bit), looking for a search bit value. When the + * search value is found, the index of that bit (where the most significant bit is + * index 0) is returned. If "search" is true, the scan will search for the bit + * value 1. If "search" is false it will search for bit value 0. + * Requires server version 5.6.0+. + * + *
{@code
+     * // lscan(a, true) == 4
+     * Exp.eq(
+     *   Exp.lscan(Exp.intBin("a"), Exp.val(true)),
+     *   Exp.val(4));
+     * }
+ */ + public static Exp lscan(Exp value, Exp search) { + return new CmdExp(INT_LSCAN, value, search); + } + + /** + * Create expression that scans integer bits from right (least significant bit) to + * left (most significant bit), looking for a search bit value. When the + * search value is found, the index of that bit (where the most significant bit is + * index 0) is returned. If "search" is true, the scan will search for the bit + * value 1. If "search" is false it will search for bit value 0. + * Requires server version 5.6.0+. + * + *
{@code
+     * // rscan(a, true) == 4
+     * Exp.eq(
+     *   Exp.rscan(Exp.intBin("a"), Exp.val(true)),
+     *   Exp.val(4));
+     * }
+ */ + public static Exp rscan(Exp value, Exp search) { + return new CmdExp(INT_RSCAN, value, search); + } + + /** + * Create expression that returns the minimum value in a variable number of expressions. + * All arguments must be the same type (integer or float). + * Requires server version 5.6.0+. + * + *
{@code
+     * // min(a, b, c) > 0
+     * Exp.gt(
+     *   Exp.min(Exp.intBin("a"), Exp.intBin("b"), Exp.intBin("c")),
+     *   Exp.val(0));
+     * }
+ */ + public static Exp min(Exp... exps) { + return new CmdExp(MIN, exps); + } + + /** + * Create expression that returns the maximum value in a variable number of expressions. + * All arguments must be the same type (integer or float). + * Requires server version 5.6.0+. + * + *
{@code
+     * // max(a, b, c) > 100
+     * Exp.gt(
+     *   Exp.max(Exp.intBin("a"), Exp.intBin("b"), Exp.intBin("c")),
+     *   Exp.val(100));
+     * }
+ */ + public static Exp max(Exp... exps) { + return new CmdExp(MAX, exps); + } + + //-------------------------------------------------- + // Variables + //-------------------------------------------------- + + /** + * Conditionally select an action expression from a variable number of expression pairs + * followed by a default action expression. Every action expression must return the same type. + * The only exception is {@link #unknown()} which can be mixed with other types. + *

+ * Requires server version 5.6.0+. + * + *

{@code
+     * Args Format: bool exp1, action exp1, bool exp2, action exp2, ..., action-default
+     *
+     * // Apply operator based on type.
+     * Exp.cond(
+     *   Exp.eq(Exp.intBin("type"), Exp.val(0)), Exp.add(Exp.intBin("val1"), Exp.intBin("val2")),
+     *   Exp.eq(Exp.intBin("type"), Exp.val(1)), Exp.sub(Exp.intBin("val1"), Exp.intBin("val2")),
+     *   Exp.eq(Exp.intBin("type"), Exp.val(2)), Exp.mul(Exp.intBin("val1"), Exp.intBin("val2")),
+     *   Exp.val(-1));
+     * }
+ */ + public static Exp cond(Exp... exps) { + return new CmdExp(COND, exps); + } + + /** + * Define variables and expressions in scope. + * Requires server version 5.6.0+. + * + *
{@code
+     * Args Format: , , ..., 
+     * def: {@link Exp#def(String, Exp)}
+     * exp: Scoped expression
+     * }
+ * + *
{@code
+     * // 5 < a < 10
+     * Exp.let(
+     *   Exp.def("x", Exp.intBin("a")),
+     *   Exp.and(
+     *     Exp.lt(Exp.val(5), Exp.var("x")),
+     *     Exp.lt(Exp.var("x"), Exp.val(10))));
+     * }
+ */ + public static Exp let(Exp... exps) { + return new Let(exps); + } + + /** + * Assign variable to a {@link Exp#let(Exp...)} expression that can be accessed later. + * Requires server version 5.6.0+. + * + *
{@code
+     * // 5 < a < 10
+     * Exp.let(
+     *   Exp.def("x", Exp.intBin("a")),
+     *   Exp.and(
+     *     Exp.lt(Exp.val(5), Exp.var("x")),
+     *     Exp.lt(Exp.var("x"), Exp.val(10))));
+     * }
+ */ + public static Exp def(String name, Exp value) { + return new Def(name, value); + } + + /** + * Retrieve expression value from a variable. + * Requires server version 5.6.0+. + * + *
{@code
+     * // 5 < a < 10
+     * Exp.let(
+     *   Exp.def("x", Exp.intBin("a")),
+     *   Exp.and(
+     *     Exp.lt(Exp.val(5), Exp.var("x")),
+     *     Exp.lt(Exp.var("x"), Exp.val(10))));
+     * }
+ */ + public static Exp var(String name) { + return new CmdStr(VAR, name); + } + + //-------------------------------------------------- + // Miscellaneous + //-------------------------------------------------- + + /** + * Create unknown value. Used to intentionally fail an expression. + * The failure can be ignored with {@link ExpWriteFlags#EVAL_NO_FAIL} + * or {@link ExpReadFlags#EVAL_NO_FAIL}. + * Requires server version 5.6.0+. + * + *
{@code
+     * // double v = balance - 100.0;
+     * // return (v > 0.0)? v : unknown;
+     * Exp.let(
+     *   Exp.def("v", Exp.sub(Exp.floatBin("balance"), Exp.val(100.0))),
+     *   Exp.cond(
+     *     Exp.ge(Exp.var("v"), Exp.val(0.0)), Exp.var("v"),
+     *     Exp.unknown()));
+     * }
+ */ + public static Exp unknown() { + return new Cmd(UNKNOWN); + } + + /** + * Merge precompiled expression into a new expression tree. + * Useful for storing common precompiled expressions and then reusing + * these expressions as part of a greater expression. + * + *
{@code
+     * // Merge precompiled expression into new expression.
+     * Expression e = Exp.build(Exp.eq(Exp.intBin("a"), Exp.val(200)));
+     * Expression merged = Exp.build(Exp.and(Exp.expr(e), Exp.eq(Exp.intBin("b"), Exp.val(100))));
+     * }
+ */ + public static Exp expr(Expression e) { + return new ExpBytes(e); + } + + //-------------------------------------------------- + // Internal + //-------------------------------------------------- + + private static final int UNKNOWN = 0; + private static final int EQ = 1; + private static final int NE = 2; + private static final int GT = 3; + private static final int GE = 4; + private static final int LT = 5; + private static final int LE = 6; + private static final int REGEX = 7; + private static final int GEO = 8; + private static final int AND = 16; + private static final int OR = 17; + private static final int NOT = 18; + private static final int EXCLUSIVE = 19; + private static final int ADD = 20; + private static final int SUB = 21; + private static final int MUL = 22; + private static final int DIV = 23; + private static final int POW = 24; + private static final int LOG = 25; + private static final int MOD = 26; + private static final int ABS = 27; + private static final int FLOOR = 28; + private static final int CEIL = 29; + private static final int TO_INT = 30; + private static final int TO_FLOAT = 31; + private static final int INT_AND = 32; + private static final int INT_OR = 33; + private static final int INT_XOR = 34; + private static final int INT_NOT = 35; + private static final int INT_LSHIFT = 36; + private static final int INT_RSHIFT = 37; + private static final int INT_ARSHIFT = 38; + private static final int INT_COUNT = 39; + private static final int INT_LSCAN = 40; + private static final int INT_RSCAN = 41; + private static final int MIN = 50; + private static final int MAX = 51; + private static final int DIGEST_MODULO = 64; + private static final int DEVICE_SIZE = 65; + private static final int LAST_UPDATE = 66; + private static final int SINCE_UPDATE = 67; + private static final int VOID_TIME = 68; + private static final int TTL = 69; + private static final int SET_NAME = 70; + private static final int KEY_EXISTS = 71; + private static final int IS_TOMBSTONE = 72; + private static final int MEMORY_SIZE = 73; + private static final int RECORD_SIZE = 74; + private static final int KEY = 80; + private static final int BIN = 81; + private static final int BIN_TYPE = 82; + private static final int COND = 123; + private static final int VAR = 124; + private static final int LET = 125; + private static final int QUOTED = 126; + private static final int CALL = 127; + public static final int MODIFY = 0x40; + private static final long NANOS_PER_MILLIS = 1000000L; + + public abstract void pack(Packer packer); + + /** + * For internal use only. + */ + static class Module extends Exp { + private final Exp bin; + private final byte[] bytes; + private final int retType; + private final int module; + + public Module(Exp bin, byte[] bytes, int retType, int module) { + this.bin = bin; + this.bytes = bytes; + this.retType = retType; + this.module = module; + } + + @Override + public void pack(Packer packer) { + packer.packArrayBegin(5); + packer.packInt(Exp.CALL); + packer.packInt(retType); + packer.packInt(module); + packer.packByteArray(bytes, 0, bytes.length); + bin.pack(packer); + } + } + + private static final class Bin extends Exp { + private final String name; + private final Type type; + + public Bin(String name, Type type) { + this.name = name; + this.type = type; + } + + @Override + public void pack(Packer packer) { + packer.packArrayBegin(3); + packer.packInt(BIN); + packer.packInt(type.code); + packer.packString(name); + } + } + + private static final class Regex extends Exp { + private final Exp bin; + private final String regex; + private final int flags; + + private Regex(Exp bin, String regex, int flags) { + this.bin = bin; + this.regex = regex; + this.flags = flags; + } + + @Override + public void pack(Packer packer) { + packer.packArrayBegin(4); + packer.packInt(REGEX); + packer.packInt(flags); + packer.packString(regex); + bin.pack(packer); + } + } + + private static final class Let extends Exp { + private final Exp[] exps; + + private Let(Exp... exps) { + this.exps = exps; + } + + @Override + public void pack(Packer packer) { + // Let wire format: LET , , , , ..., + int count = (exps.length - 1) * 2 + 2; + packer.packArrayBegin(count); + packer.packInt(LET); + + for (Exp exp : exps) { + exp.pack(packer); + } + } + } + + private static final class Def extends Exp { + private final String name; + private final Exp exp; + + private Def(String name, Exp exp) { + this.name = name; + this.exp = exp; + } + + @Override + public void pack(Packer packer) { + packer.packString(name); + exp.pack(packer); + } + } + + private static final class CmdExp extends Exp { + private final Exp[] exps; + private final int cmd; + + private CmdExp(int cmd, Exp... exps) { + this.exps = exps; + this.cmd = cmd; + } + + @Override + public void pack(Packer packer) { + packer.packArrayBegin(exps.length + 1); + packer.packInt(cmd); + + for (Exp exp : exps) { + exp.pack(packer); + } + } + } + + private static final class CmdInt extends Exp { + private final int cmd; + private final int val; + + private CmdInt(int cmd, int val) { + this.cmd = cmd; + this.val = val; + } + + @Override + public void pack(Packer packer) { + packer.packArrayBegin(2); + packer.packInt(cmd); + packer.packInt(val); + } + } + + private static final class CmdStr extends Exp { + private final String str; + private final int cmd; + + private CmdStr(int cmd, String str) { + this.str = str; + this.cmd = cmd; + } + + @Override + public void pack(Packer packer) { + packer.packArrayBegin(2); + packer.packInt(cmd); + packer.packString(str); + } + } + + private static final class Cmd extends Exp { + private final int cmd; + + private Cmd(int cmd) { + this.cmd = cmd; + } + + @Override + public void pack(Packer packer) { + packer.packArrayBegin(1); + packer.packInt(cmd); + } + } + + private static final class Bool extends Exp { + private final boolean val; + + private Bool(boolean val) { + this.val = val; + } + + @Override + public void pack(Packer packer) { + packer.packBoolean(val); + } + } + + private static final class Int extends Exp { + private final long val; + + private Int(long val) { + this.val = val; + } + + @Override + public void pack(Packer packer) { + packer.packLong(val); + } + } + + private static final class Float extends Exp { + private final double val; + + private Float(double val) { + this.val = val; + } + + @Override + public void pack(Packer packer) { + packer.packDouble(val); + + } + } + + private static final class Str extends Exp { + private final String val; + + private Str(String val) { + this.val = val; + } + + @Override + public void pack(Packer packer) { + packer.packParticleString(val); + } + } + + private static final class Geo extends Exp { + private final String val; + + private Geo(String val) { + this.val = val; + } + + @Override + public void pack(Packer packer) { + packer.packGeoJSON(val); + } + } + + private static final class Blob extends Exp { + private final byte[] val; + + private Blob(byte[] val) { + this.val = val; + } + + @Override + public void pack(Packer packer) { + packer.packParticleBytes(val); + + } + } + + private static final class ListVal extends Exp { + private final List list; + + private ListVal(List list) { + this.list = list; + } + + @Override + public void pack(Packer packer) { + // List values need an extra array and QUOTED in order to distinguish + // between a multiple argument array call and a local list. + packer.packArrayBegin(2); + packer.packInt(QUOTED); + packer.packList(list); + } + } + + private static final class MapVal extends Exp { + private final Map map; + + private MapVal(Map map) { + this.map = map; + } + + @Override + public void pack(Packer packer) { + packer.packMap(map); + } + } + + private static final class Nil extends Exp { + @Override + public void pack(Packer packer) { + packer.packNil(); + } + } + + private static final class Infinity extends Exp { + @Override + public void pack(Packer packer) { + packer.packInfinity(); + } + } + + private static final class Wildcard extends Exp { + @Override + public void pack(Packer packer) { + packer.packWildcard(); + } + } + + private static final class ExpBytes extends Exp + { + private final byte[] bytes; + + private ExpBytes(Expression e) + { + this.bytes = e.getBytes(); + } + + @Override + public void pack(Packer packer) { + packer.packByteArray(bytes, 0, bytes.length); + } + } +} diff --git a/src/main/java/com/aerospike/dsl/client/exp/ExpReadFlags.java b/src/main/java/com/aerospike/dsl/client/exp/ExpReadFlags.java new file mode 100644 index 0000000..8a7f6ae --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/exp/ExpReadFlags.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2021 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.exp; + +/** + * Expression read flags. + */ +public final class ExpReadFlags { + /** + * Default. + */ + public static final int DEFAULT = 0; + + /** + * Ignore failures caused by the expression resolving to unknown or a non-bin type. + */ + public static final int EVAL_NO_FAIL = 16; +} diff --git a/src/main/java/com/aerospike/dsl/client/exp/ExpWriteFlags.java b/src/main/java/com/aerospike/dsl/client/exp/ExpWriteFlags.java new file mode 100644 index 0000000..20b4133 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/exp/ExpWriteFlags.java @@ -0,0 +1,65 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.exp; + +import com.aerospike.dsl.client.ResultCode; + +/** + * Expression write bit flags. Use BITWISE OR to combine flags. Example: + * + *
{@code
+ * int flags = ExpWriteFlags.CREATE_ONLY | ExpWriteFlags.POLICY_NO_FAIL;
+ * }
+ */ +public final class ExpWriteFlags { + /** + * Default. Allow create or update. + */ + public static final int DEFAULT = 0; + + /** + * If bin does not exist, a new bin will be created. + * If bin exists, the operation will be denied. + * If bin exists, fail with {@link ResultCode#BIN_EXISTS_ERROR} + * when {@link #POLICY_NO_FAIL} is not set. + */ + public static final int CREATE_ONLY = 1; + + /** + * If bin exists, the bin will be overwritten. + * If bin does not exist, the operation will be denied. + * If bin does not exist, fail with {@link ResultCode#BIN_NOT_FOUND} + * when {@link #POLICY_NO_FAIL} is not set. + */ + public static final int UPDATE_ONLY = 2; + + /** + * If expression results in nil value, then delete the bin. Otherwise, fail with + * {@link ResultCode#OP_NOT_APPLICABLE} when {@link #POLICY_NO_FAIL} is not set. + */ + public static final int ALLOW_DELETE = 4; + + /** + * Do not raise error if operation is denied. + */ + public static final int POLICY_NO_FAIL = 8; + + /** + * Ignore failures caused by the expression resolving to unknown or a non-bin type. + */ + public static final int EVAL_NO_FAIL = 16; +} diff --git a/src/main/java/com/aerospike/dsl/client/exp/Expression.java b/src/main/java/com/aerospike/dsl/client/exp/Expression.java new file mode 100644 index 0000000..51edf2f --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/exp/Expression.java @@ -0,0 +1,121 @@ +/* + * Copyright 2012-2025 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.exp; + +import com.aerospike.dsl.client.command.Command; +import com.aerospike.dsl.client.util.Crypto; +import com.aerospike.dsl.client.util.Packer; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * Packed expression byte instructions. + */ +public final class Expression implements Serializable { + private static final long serialVersionUID = 1L; + + private final byte[] bytes; + + /** + * Expression constructor used by {@link Exp#build(Exp)} + */ + Expression(Exp exp) { + Packer packer = new Packer(); + exp.pack(packer); + bytes = packer.toByteArray(); + /* + for (int i = 0; i < bytes.length; i++) { + int b = bytes[i] & 0xff; + System.out.println("" + b); + } + */ + } + + /** + * Expression constructor for packed expression instructions. + */ + Expression(byte[] bytes) { + this.bytes = bytes; + } + + /** + * Return a new expression from packed expression instructions in bytes. + */ + public static Expression fromBytes(byte[] bytes) { + return new Expression(bytes); + } + + /** + * Return a new expression from packed expression instructions in base64 encoded bytes. + */ + public static Expression fromBase64(byte[] bytes) { + return Expression.fromBytes(Crypto.decodeBase64(bytes, 0, bytes.length)); + } + + /** + * Return a new expression from packed expression instructions in base64 encoded string. + */ + public static Expression fromBase64(String s) { + return Expression.fromBase64(s.getBytes()); + } + + /** + * Return packed byte instructions. + */ + public byte[] getBytes() { + return bytes; + } + + /** + * Return byte instructions in base64 encoding. + */ + public String getBase64() { + return Crypto.encodeBase64(bytes); + } + + /** + * Estimate expression size in wire protocol. + * For internal use only. + */ + public int size() { + return bytes.length + Command.FIELD_HEADER_SIZE; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(bytes); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Expression other = (Expression) obj; + return Arrays.equals(bytes, other.bytes); + } +} diff --git a/src/main/java/com/aerospike/dsl/client/exp/ListExp.java b/src/main/java/com/aerospike/dsl/client/exp/ListExp.java new file mode 100644 index 0000000..2173b23 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/exp/ListExp.java @@ -0,0 +1,328 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.exp; + +import com.aerospike.dsl.client.AerospikeException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.util.Pack; +import com.aerospike.dsl.client.util.Packer; + +/** + * List expression generator. See {@link Exp}. + *

+ * The bin expression argument in these methods can be a reference to a bin or the + * result of another expression. Expressions that modify bin values are only used + * for temporary expression evaluation and are not permanently applied to the bin. + *

+ * List modify expressions return the bin's value. This value will be a list except + * when the list is nested within a map. In that case, a map is returned for the + * list modify expression. + *

+ * List expressions support negative indexing. If the index is negative, the + * resolved index starts backwards from end of list. If an index is out of bounds, + * a parameter error will be returned. If a range is partially out of bounds, the + * valid part of the range will be returned. Index/Range examples: + *

    + *
  • Index 0: First item in list.
  • + *
  • Index 4: Fifth item in list.
  • + *
  • Index -1: Last item in list.
  • + *
  • Index -3: Third to last item in list.
  • + *
  • Index 1 Count 2: Second and third items in list.
  • + *
  • Index -3 Count 3: Last three items in list.
  • + *
  • Index -5 Count 4: Range between fifth to last item to second to last item inclusive.
  • + *
+ *

+ * Nested expressions are supported by optional CTX context arguments. Example: + *

    + *
  • bin = [[7,9,5],[1,2,3],[6,5,4,1]]
  • + *
  • Get size of last list.
  • + *
  • ListExp.size(Exp.listBin("bin"), CTX.listIndex(-1))
  • + *
  • result = 4
  • + *
+ */ +public final class ListExp { + private static final int MODULE = 0; + private static final int APPEND = 1; + private static final int APPEND_ITEMS = 2; + private static final int INSERT = 3; + private static final int INSERT_ITEMS = 4; + private static final int SET = 9; + private static final int CLEAR = 11; + private static final int INCREMENT = 12; + private static final int SORT = 13; + private static final int SIZE = 16; + private static final int GET_BY_INDEX = 19; + private static final int GET_BY_RANK = 21; + private static final int GET_BY_VALUE = 22; // GET_ALL_BY_VALUE on server. + private static final int GET_BY_VALUE_LIST = 23; + private static final int GET_BY_INDEX_RANGE = 24; + private static final int GET_BY_VALUE_INTERVAL = 25; + private static final int GET_BY_RANK_RANGE = 26; + private static final int GET_BY_VALUE_REL_RANK_RANGE = 27; + private static final int REMOVE_BY_INDEX = 32; + private static final int REMOVE_BY_RANK = 34; + private static final int REMOVE_BY_VALUE = 35; + private static final int REMOVE_BY_VALUE_LIST = 36; + private static final int REMOVE_BY_INDEX_RANGE = 37; + private static final int REMOVE_BY_VALUE_INTERVAL = 38; + private static final int REMOVE_BY_RANK_RANGE = 39; + private static final int REMOVE_BY_VALUE_REL_RANK_RANGE = 40; + + /** + * Create expression that returns list size. + * + *
{@code
+     * // List bin "a" size > 7
+     * Exp.gt(ListExp.size(Exp.listBin("a")), Exp.val(7))
+     * }
+ */ + public static Exp size(Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(SIZE, ctx); + return addRead(bin, bytes, Exp.Type.INT); + } + + /** + * Create expression that selects list items identified by value and returns selected + * data specified by returnType. + * + *
{@code
+     * // List bin "a" contains at least one item == "abc"
+     * ListExp.getByValue(ListReturnType.EXISTS, Exp.val("abc"), Exp.listBin("a"))
+     * }
+ * + * @param returnType metadata attributes to return. See {@link ListReturnType} + * @param value search expression + * @param bin list bin or list value expression + * @param ctx optional context path for nested CDT + */ + public static Exp getByValue(int returnType, Exp value, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE, returnType, value, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects list items identified by value range and returns selected data + * specified by returnType. + * + *
{@code
+     * // List bin "a" items >= 10 && items < 20
+     * ListExp.getByValueRange(ListReturnType.VALUE, Exp.val(10), Exp.val(20), Exp.listBin("a"))
+     * }
+ * + * @param returnType metadata attributes to return. See {@link ListReturnType} + * @param valueBegin begin expression inclusive. If null, range is less than valueEnd. + * @param valueEnd end expression exclusive. If null, range is greater than equal to valueBegin. + * @param bin bin or list value expression + * @param ctx optional context path for nested CDT + */ + public static Exp getByValueRange(int returnType, Exp valueBegin, Exp valueEnd, Exp bin, CTX... ctx) { + byte[] bytes = ListExp.packRangeOperation(GET_BY_VALUE_INTERVAL, returnType, valueBegin, valueEnd, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects list items identified by values and returns selected data + * specified by returnType. + */ + public static Exp getByValueList(int returnType, Exp values, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE_LIST, returnType, values, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects list items nearest to value and greater by relative rank + * and returns selected data specified by returnType (See {@link ListReturnType}). + *

+ * Examples for ordered list [0,4,5,9,11,15]: + *

    + *
  • (value,rank) = [selected items]
  • + *
  • (5,0) = [5,9,11,15]
  • + *
  • (5,1) = [9,11,15]
  • + *
  • (5,-1) = [4,5,9,11,15]
  • + *
  • (3,0) = [4,5,9,11,15]
  • + *
  • (3,3) = [11,15]
  • + *
  • (3,-3) = [0,4,5,9,11,15]
  • + *
+ */ + public static Exp getByValueRelativeRankRange(int returnType, Exp value, Exp rank, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE_REL_RANK_RANGE, returnType, value, rank, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects list items nearest to value and greater by relative rank with a count limit + * and returns selected data specified by returnType (See {@link ListReturnType}). + *

+ * Examples for ordered list [0,4,5,9,11,15]: + *

    + *
  • (value,rank,count) = [selected items]
  • + *
  • (5,0,2) = [5,9]
  • + *
  • (5,1,1) = [9]
  • + *
  • (5,-1,2) = [4,5]
  • + *
  • (3,0,1) = [4]
  • + *
  • (3,3,7) = [11,15]
  • + *
  • (3,-3,2) = []
  • + *
+ */ + public static Exp getByValueRelativeRankRange(int returnType, Exp value, Exp rank, Exp count, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE_REL_RANK_RANGE, returnType, value, rank, count, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects list item identified by index and returns + * selected data specified by returnType. + * + *
{@code
+     * // a[3] == 5
+     * Exp.eq(
+     *   ListExp.getByIndex(ListReturnType.VALUE, Exp.Type.INT, Exp.val(3), Exp.listBin("a")),
+     *   Exp.val(5));
+     * }
+ * + * @param returnType metadata attributes to return. See {@link ListReturnType} + * @param valueType expected type of return value + * @param index list index expression + * @param bin list bin or list value expression + * @param ctx optional context path for nested CDT + */ + public static Exp getByIndex(int returnType, Exp.Type valueType, Exp index, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_INDEX, returnType, index, ctx); + return addRead(bin, bytes, valueType); + } + + /** + * Create expression that selects list items starting at specified index to the end of list + * and returns selected data specified by returnType (See {@link ListReturnType}). + */ + public static Exp getByIndexRange(int returnType, Exp index, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_INDEX_RANGE, returnType, index, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects "count" list items starting at specified index + * and returns selected data specified by returnType (See {@link ListReturnType}). + */ + public static Exp getByIndexRange(int returnType, Exp index, Exp count, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_INDEX_RANGE, returnType, index, count, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects list item identified by rank and returns selected + * data specified by returnType. + * + *
{@code
+     * // Player with lowest score.
+     * ListExp.getByRank(ListReturnType.VALUE, Type.STRING, Exp.val(0), Exp.listBin("a"))
+     * }
+ * + * @param returnType metadata attributes to return. See {@link ListReturnType} + * @param valueType expected type of return value + * @param rank rank expression + * @param bin list bin or list value expression + * @param ctx optional context path for nested CDT + */ + public static Exp getByRank(int returnType, Exp.Type valueType, Exp rank, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_RANK, returnType, rank, ctx); + return addRead(bin, bytes, valueType); + } + + /** + * Create expression that selects list items starting at specified rank to the last ranked item + * and returns selected data specified by returnType (See {@link ListReturnType}). + */ + public static Exp getByRankRange(int returnType, Exp rank, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_RANK_RANGE, returnType, rank, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects "count" list items starting at specified rank and returns + * selected data specified by returnType (See {@link ListReturnType}). + */ + public static Exp getByRankRange(int returnType, Exp rank, Exp count, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_RANK_RANGE, returnType, rank, count, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + private static Exp addWrite(Exp bin, byte[] bytes, CTX[] ctx) { + int retType; + + if (ctx == null || ctx.length == 0) { + retType = Exp.Type.LIST.code; + } + else { + retType = ((ctx[0].id & 0x10) == 0)? Exp.Type.MAP.code : Exp.Type.LIST.code; + } + return new Exp.Module(bin, bytes, retType, MODULE | Exp.MODIFY); + } + + private static Exp addRead(Exp bin, byte[] bytes, Exp.Type retType) { + return new Exp.Module(bin, bytes, retType.code, MODULE); + } + + private static Exp.Type getValueType(int returnType) { + int t = returnType & ~ListReturnType.INVERTED; + + switch (t) { + case ListReturnType.INDEX: + case ListReturnType.REVERSE_INDEX: + case ListReturnType.RANK: + case ListReturnType.REVERSE_RANK: + // This method only called from expressions that can return multiple integers (ie list). + return Exp.Type.LIST; + + case ListReturnType.COUNT: + return Exp.Type.INT; + + case ListReturnType.VALUE: + // This method only called from expressions that can return multiple objects (ie list). + return Exp.Type.LIST; + + case ListReturnType.EXISTS: + return Exp.Type.BOOL; + + default: + case ListReturnType.NONE: + throw new AerospikeException("Invalid ListReturnType: " + returnType); + } + } + + protected static byte[] packRangeOperation(int command, int returnType, Exp begin, Exp end, CTX[] ctx) { + Packer packer = new Packer(); + Pack.init(packer, ctx); + packer.packArrayBegin((end != null)? 4 : 3); + packer.packInt(command); + packer.packInt(returnType); + + if (begin != null) { + begin.pack(packer); + } + else { + packer.packNil(); + } + + if (end != null) { + end.pack(packer); + } + return packer.toByteArray(); + } + +} diff --git a/src/main/java/com/aerospike/dsl/client/exp/MapExp.java b/src/main/java/com/aerospike/dsl/client/exp/MapExp.java new file mode 100644 index 0000000..8fe82c4 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/exp/MapExp.java @@ -0,0 +1,378 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.exp; + +import com.aerospike.dsl.client.AerospikeException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.util.Pack; + +/** + * Map expression generator. See {@link Exp}. + *

+ * The bin expression argument in these methods can be a reference to a bin or the + * result of another expression. Expressions that modify bin values are only used + * for temporary expression evaluation and are not permanently applied to the bin. + *

+ * Map modify expressions return the bin's value. This value will be a map except + * when the map is nested within a list. In that case, a list is returned for the + * map modify expression. + *

+ * Valid map key types are: + *

    + *
  • String
  • + *
  • Integer
  • + *
  • byte[]
  • + *
+ *

+ * The server will validate map key types in an upcoming release. + *

+ * All maps maintain an index and a rank. The index is the item offset from the start of the map, + * for both unordered and ordered maps. The rank is the sorted index of the value component. + * Map supports negative indexing for index and rank. + *

+ * Index examples: + *

    + *
  • Index 0: First item in map.
  • + *
  • Index 4: Fifth item in map.
  • + *
  • Index -1: Last item in map.
  • + *
  • Index -3: Third to last item in map.
  • + *
  • Index 1 Count 2: Second and third items in map.
  • + *
  • Index -3 Count 3: Last three items in map.
  • + *
  • Index -5 Count 4: Range between fifth to last item to second to last item inclusive.
  • + *
+ *

+ * Rank examples: + *

    + *
  • Rank 0: Item with lowest value rank in map.
  • + *
  • Rank 4: Fifth lowest ranked item in map.
  • + *
  • Rank -1: Item with highest ranked value in map.
  • + *
  • Rank -3: Item with third highest ranked value in map.
  • + *
  • Rank 1 Count 2: Second and third lowest ranked items in map.
  • + *
  • Rank -3 Count 3: Top three ranked items in map.
  • + *
+ *

+ * Nested expressions are supported by optional CTX context arguments. Example: + *

    + *
  • bin = {key1={key11=9,key12=4}, key2={key21=3,key22=5}}
  • + *
  • Set map value to 11 for map key "key21" inside of map key "key2".
  • + *
  • Get size of map key2.
  • + *
  • MapExp.size(Exp.mapBin("bin"), CTX.mapKey(Value.get("key2"))
  • + *
  • result = 2
  • + *
+ */ +public final class MapExp { + private static final int MODULE = 0; + private static final int PUT = 67; + private static final int PUT_ITEMS = 68; + private static final int REPLACE = 69; + private static final int REPLACE_ITEMS = 70; + private static final int INCREMENT = 73; + private static final int CLEAR = 75; + private static final int REMOVE_BY_KEY = 76; + private static final int REMOVE_BY_INDEX = 77; + private static final int REMOVE_BY_RANK = 79; + private static final int REMOVE_BY_KEY_LIST = 81; + private static final int REMOVE_BY_VALUE = 82; + private static final int REMOVE_BY_VALUE_LIST = 83; + private static final int REMOVE_BY_KEY_INTERVAL = 84; + private static final int REMOVE_BY_INDEX_RANGE = 85; + private static final int REMOVE_BY_VALUE_INTERVAL = 86; + private static final int REMOVE_BY_RANK_RANGE = 87; + private static final int REMOVE_BY_KEY_REL_INDEX_RANGE = 88; + private static final int REMOVE_BY_VALUE_REL_RANK_RANGE = 89; + private static final int SIZE = 96; + private static final int GET_BY_KEY = 97; + private static final int GET_BY_INDEX = 98; + private static final int GET_BY_RANK = 100; + private static final int GET_BY_VALUE = 102; // GET_ALL_BY_VALUE on server. + private static final int GET_BY_KEY_INTERVAL = 103; + private static final int GET_BY_INDEX_RANGE = 104; + private static final int GET_BY_VALUE_INTERVAL = 105; + private static final int GET_BY_RANK_RANGE = 106; + private static final int GET_BY_KEY_LIST = 107; + private static final int GET_BY_VALUE_LIST = 108; + private static final int GET_BY_KEY_REL_INDEX_RANGE = 109; + private static final int GET_BY_VALUE_REL_RANK_RANGE = 110; + + /** + * Create expression that returns list size. + * + *
{@code
+     * // Map bin "a" size > 7
+     * Exp.gt(MapExp.size(Exp.mapBin("a")), Exp.val(7))
+     * }
+ */ + public static Exp size(Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(SIZE, ctx); + return addRead(bin, bytes, Exp.Type.INT); + } + + /** + * Create expression that selects map item identified by key and returns selected data + * specified by returnType. + * + *
{@code
+     * // Map bin "a" contains key "B"
+     * MapExp.getByKey(MapReturnType.EXISTS, Exp.Type.BOOL, Exp.val("B"), Exp.mapBin("a"))
+     * }
+ * + * @param returnType metadata attributes to return. See {@link MapReturnType} + * @param valueType expected type of return value + * @param key map key expression + * @param bin bin or map value expression + * @param ctx optional context path for nested CDT + */ + public static Exp getByKey(int returnType, Exp.Type valueType, Exp key, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_KEY, returnType, key, ctx); + return addRead(bin, bytes, valueType); + } + + /** + * Create expression that selects map items identified by key range (keyBegin inclusive, keyEnd exclusive). + * If keyBegin is null, the range is less than keyEnd. + * If keyEnd is null, the range is greater than equal to keyBegin. + *

+ * Expression returns selected data specified by returnType (See {@link MapReturnType}). + */ + public static Exp getByKeyRange(int returnType, Exp keyBegin, Exp keyEnd, Exp bin, CTX... ctx) { + byte[] bytes = ListExp.packRangeOperation(GET_BY_KEY_INTERVAL, returnType, keyBegin, keyEnd, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items identified by keys and returns selected data specified by + * returnType (See {@link MapReturnType}). + */ + public static Exp getByKeyList(int returnType, Exp keys, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_KEY_LIST, returnType, keys, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items nearest to key and greater by index. + * Expression returns selected data specified by returnType (See {@link MapReturnType}). + *

+ * Examples for ordered map [{0=17},{4=2},{5=15},{9=10}]: + *

    + *
  • (value,index) = [selected items]
  • + *
  • (5,0) = [{5=15},{9=10}]
  • + *
  • (5,1) = [{9=10}]
  • + *
  • (5,-1) = [{4=2},{5=15},{9=10}]
  • + *
  • (3,2) = [{9=10}]
  • + *
  • (3,-2) = [{0=17},{4=2},{5=15},{9=10}]
  • + *
+ */ + public static Exp getByKeyRelativeIndexRange(int returnType, Exp key, Exp index, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_KEY_REL_INDEX_RANGE, returnType, key, index, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items nearest to key and greater by index with a count limit. + * Expression returns selected data specified by returnType (See {@link MapReturnType}). + *

+ * Examples for ordered map [{0=17},{4=2},{5=15},{9=10}]: + *

    + *
  • (value,index,count) = [selected items]
  • + *
  • (5,0,1) = [{5=15}]
  • + *
  • (5,1,2) = [{9=10}]
  • + *
  • (5,-1,1) = [{4=2}]
  • + *
  • (3,2,1) = [{9=10}]
  • + *
  • (3,-2,2) = [{0=17}]
  • + *
+ */ + public static Exp getByKeyRelativeIndexRange(int returnType, Exp key, Exp index, Exp count, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_KEY_REL_INDEX_RANGE, returnType, key, index, count, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items identified by value and returns selected data + * specified by returnType. + * + *
{@code
+     * // Map bin "a" contains value "BBB"
+     * MapExp.getByValue(MapReturnType.EXISTS, Exp.val("BBB"), Exp.mapBin("a"))
+     * }
+ * + * @param returnType metadata attributes to return. See {@link MapReturnType} + * @param value value expression + * @param bin bin or map value expression + * @param ctx optional context path for nested CDT + */ + public static Exp getByValue(int returnType, Exp value, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE, returnType, value, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items identified by value range (valueBegin inclusive, valueEnd exclusive) + * If valueBegin is null, the range is less than valueEnd. + * If valueEnd is null, the range is greater than equal to valueBegin. + *

+ * Expression returns selected data specified by returnType (See {@link MapReturnType}). + */ + public static Exp getByValueRange(int returnType, Exp valueBegin, Exp valueEnd, Exp bin, CTX... ctx) { + byte[] bytes = ListExp.packRangeOperation(GET_BY_VALUE_INTERVAL, returnType, valueBegin, valueEnd, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items identified by values and returns selected data specified by + * returnType (See {@link MapReturnType}). + */ + public static Exp getByValueList(int returnType, Exp values, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE_LIST, returnType, values, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items nearest to value and greater by relative rank. + * Expression returns selected data specified by returnType (See {@link MapReturnType}). + *

+ * Examples for map [{4=2},{9=10},{5=15},{0=17}]: + *

    + *
  • (value,rank) = [selected items]
  • + *
  • (11,1) = [{0=17}]
  • + *
  • (11,-1) = [{9=10},{5=15},{0=17}]
  • + *
+ */ + public static Exp getByValueRelativeRankRange(int returnType, Exp value, Exp rank, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE_REL_RANK_RANGE, returnType, value, rank, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map items nearest to value and greater by relative rank with a count limit. + * Expression returns selected data specified by returnType (See {@link MapReturnType}). + *

+ * Examples for map [{4=2},{9=10},{5=15},{0=17}]: + *

    + *
  • (value,rank,count) = [selected items]
  • + *
  • (11,1,1) = [{0=17}]
  • + *
  • (11,-1,1) = [{9=10}]
  • + *
+ */ + public static Exp getByValueRelativeRankRange(int returnType, Exp value, Exp rank, Exp count, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_VALUE_REL_RANK_RANGE, returnType, value, rank, count, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map item identified by index and returns selected data specified by + * returnType (See {@link MapReturnType}). + */ + public static Exp getByIndex(int returnType, Exp.Type valueType, Exp index, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_INDEX, returnType, index, ctx); + return addRead(bin, bytes, valueType); + } + + /** + * Create expression that selects map items starting at specified index to the end of map and returns selected + * data specified by returnType (See {@link MapReturnType}). + */ + public static Exp getByIndexRange(int returnType, Exp index, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_INDEX_RANGE, returnType, index, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects "count" map items starting at specified index and returns selected data + * specified by returnType (See {@link MapReturnType}). + */ + public static Exp getByIndexRange(int returnType, Exp index, Exp count, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_INDEX_RANGE, returnType, index, count, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects map item identified by rank and returns selected data specified by + * returnType (See {@link MapReturnType}). + */ + public static Exp getByRank(int returnType, Exp.Type valueType, Exp rank, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_RANK, returnType, rank, ctx); + return addRead(bin, bytes, valueType); + } + + /** + * Create expression that selects map items starting at specified rank to the last ranked item and + * returns selected data specified by returnType (See {@link MapReturnType}). + */ + public static Exp getByRankRange(int returnType, Exp rank, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_RANK_RANGE, returnType, rank, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + /** + * Create expression that selects "count" map items starting at specified rank and returns selected + * data specified by returnType (See {@link MapReturnType}). + */ + public static Exp getByRankRange(int returnType, Exp rank, Exp count, Exp bin, CTX... ctx) { + byte[] bytes = Pack.pack(GET_BY_RANK_RANGE, returnType, rank, count, ctx); + return addRead(bin, bytes, getValueType(returnType)); + } + + private static Exp addWrite(Exp bin, byte[] bytes, CTX[] ctx) { + int retType; + + if (ctx == null || ctx.length == 0) { + retType = Exp.Type.MAP.code; + } + else { + retType = ((ctx[0].id & 0x10) == 0)? Exp.Type.MAP.code : Exp.Type.LIST.code; + } + return new Exp.Module(bin, bytes, retType, MODULE | Exp.MODIFY); + } + + private static Exp addRead(Exp bin, byte[] bytes, Exp.Type retType) { + return new Exp.Module(bin, bytes, retType.code, MODULE); + } + + private static Exp.Type getValueType(int returnType) { + int t = returnType & ~MapReturnType.INVERTED; + + switch (t) { + case MapReturnType.INDEX: + case MapReturnType.REVERSE_INDEX: + case MapReturnType.RANK: + case MapReturnType.REVERSE_RANK: + // This method only called from expressions that can return multiple integers (ie list). + return Exp.Type.LIST; + + case MapReturnType.COUNT: + return Exp.Type.INT; + + case MapReturnType.KEY: + case MapReturnType.VALUE: + // This method only called from expressions that can return multiple objects (ie list). + return Exp.Type.LIST; + + case MapReturnType.KEY_VALUE: + case MapReturnType.ORDERED_MAP: + case MapReturnType.UNORDERED_MAP: + return Exp.Type.MAP; + + case MapReturnType.EXISTS: + return Exp.Type.BOOL; + + default: + case MapReturnType.NONE: + throw new AerospikeException("Invalid MapReturnType: " + returnType); + } + } +} diff --git a/src/main/java/com/aerospike/dsl/client/query/Filter.java b/src/main/java/com/aerospike/dsl/client/query/Filter.java new file mode 100644 index 0000000..5d22c4a --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/query/Filter.java @@ -0,0 +1,799 @@ +/* + * Copyright 2012-2025 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.query; + +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.command.Buffer; +import com.aerospike.dsl.client.command.ParticleType; +import com.aerospike.dsl.client.exp.Expression; +import com.aerospike.dsl.client.util.Pack; +import lombok.Getter; + +import java.util.Arrays; + +/** + * Query filter definition. + * + * Currently, only one filter is allowed in a Statement, and must be on bin which has a secondary index defined. + */ +@Getter +public final class Filter { + + FilterType filterType; + + public enum FilterType { + EQ, EQ_BY_INDEX, CONTAINS, CONTAINS_BY_INDEX, RANGE, RANGE_BY_INDEX, + GEO_CONTAINS, GEO_CONTAINS_BY_INDEX, GEO_WITHIN_REGION, GEO_WITHIN_REGION_BY_INDEX, + GEO_WITHIN_RADIUS, GEO_WITHIN_RADIUS_BY_INDEX + } + + + /** + * Create long equality filter for query. + * + * @param name bin name + * @param value filter value + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter equal(String name, long value, CTX... ctx) { + Value val = Value.get(value); + return new Filter(FilterType.EQ, name, IndexCollectionType.DEFAULT, val.getType(), val, val, ctx); + } + + /** + * Create long equality filter for query with Expression. + * + * @param exp the Expression + * @param value filter value + * @return filter instance + */ + public static Filter equal(Expression exp, long value) { + Value val = Value.get(value); + return new Filter(FilterType.EQ, null, exp.getBytes(), IndexCollectionType.DEFAULT, val.getType(), val, val); + } + + /** + * Create long equality filter for query with index name. + * + * @param indexName the name that was assigned to the Secondary Index (SI) + * @param value filter value + * @return filter instance + */ + public static Filter equalByIndex(String indexName, long value) { + Value val = Value.get(value); + return new Filter(FilterType.EQ_BY_INDEX, indexName, null, IndexCollectionType.DEFAULT, val.getType(), val, val); + } + + /** + * Create string equality filter for query. + * + * @param name bin name + * @param value filter value + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter equal(String name, String value, CTX... ctx) { + Value val = Value.get(value); + return new Filter(FilterType.EQ, name, IndexCollectionType.DEFAULT, val.getType(), val, val, ctx); + } + + /** + * Create string equality filter for query with Expression. + * + * @param exp the Expression + * @param value filter value + * @return filter instance + */ + public static Filter equal(Expression exp, String value) { + Value val = Value.get(value); + return new Filter(FilterType.EQ, null, exp.getBytes(), IndexCollectionType.DEFAULT, val.getType(), val, val); + } + + /** + * Create string equality filter for query with index name. + * + * @param indexName the name that was assigned to the Secondary Index (SI) + * @param value filter value + * @return filter instance + */ + public static Filter equalByIndex(String indexName, String value) { + Value val = Value.get(value); + return new Filter(FilterType.EQ_BY_INDEX, indexName, null, IndexCollectionType.DEFAULT, val.getType(), val, val); + } + + /** + * Create blob equality filter for query. + * Requires server version 7.0+. + * + * @param name bin name + * @param value filter value + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter equal(String name, byte[] value, CTX... ctx) { + Value val = Value.get(value); + return new Filter(FilterType.EQ, name, IndexCollectionType.DEFAULT, val.getType(), val, val, ctx); + } + + /** + * Create blob equality filter for query with Expression. + * + * @param exp the Expression + * @param value filter value + * @return filter instance + */ + public static Filter equal(Expression exp, byte[] value) { + Value val = Value.get(value); + return new Filter(FilterType.EQ, null, exp.getBytes(), IndexCollectionType.DEFAULT, val.getType(), val, val); + } + + /** + * Create blob equality filter for query with index name. + * + * @param indexName the name that was assigned to the Secondary Index (SI) + * @param value filter value + * @return filter instance + */ + public static Filter equalByIndex(String indexName, byte[] value) { + Value val = Value.get(value); + return new Filter(FilterType.EQ_BY_INDEX, indexName, null, IndexCollectionType.DEFAULT, val.getType(), val, val); + } + + /** + * Create contains number filter for query on collection index. + * + * @param name bin name + * @param type index collection type + * @param value filter value + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter contains(String name, IndexCollectionType type, long value, CTX... ctx) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS, name, type, val.getType(), val, val, ctx); + } + + /** + * Create contains number filter for query on collection index with Expression. + * + * @param exp expression to use for the filter + * @param type index collection type + * @param value filter value + * @return filter instance + */ + public static Filter contains(Expression exp, IndexCollectionType type, long value) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS, null, exp.getBytes(), type, val.getType(), val, val); + } + + /** + * Create contains number filter for query on collection index with index name. + * + * @param indexName index name + * @param type index collection type + * @param value filter value + * @return filter instance + */ + public static Filter containsByIndex(String indexName, IndexCollectionType type, long value) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS_BY_INDEX, indexName, null, type, val.getType(), val, val); + } + + /** + * Create contains string filter for query on collection index. + * + * @param name bin name + * @param type index collection type + * @param value filter value + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter contains(String name, IndexCollectionType type, String value, CTX... ctx) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS, name, type, val.getType(), val, val, ctx); + } + + /** + * Create contains string filter for query on collection index with Expression. + * + * @param exp expression to use for the filter + * @param type index collection type + * @param value filter value + * @return filter instance + */ + public static Filter contains(Expression exp, IndexCollectionType type, String value) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS, null, exp.getBytes(), type, val.getType(), val, val); + } + + /** + * Create contains string filter for query on collection index with index name. + * + * @param indexName index name + * @param type index collection type + * @param value filter value + * @return filter instance + */ + public static Filter containsByIndex(String indexName, IndexCollectionType type, String value) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS_BY_INDEX, indexName, null, type, val.getType(), val, val); + } + + /** + * Create contains byte[] filter for query on collection index. + * + * @param name bin name + * @param type index collection type + * @param value filter value + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter contains(String name, IndexCollectionType type, byte[] value, CTX... ctx) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS, name, type, val.getType(), val, val, ctx); + } + + /** + * Create contains byte[] filter for query on collection index with expression. + * + * @param exp expression to use for the filter + * @param type index collection type + * @param value filter value + * @return filter instance + */ + public static Filter contains(Expression exp, IndexCollectionType type, byte[] value) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS, null, exp.getBytes(), type, val.getType(), val, val); + } + + /** + * Create contains byte[] filter for query on collection index with index name. + * + * @param indexName index name + * @param type index collection type + * @param value filter value + * @return filter instance + */ + public static Filter containsByIndex(String indexName, IndexCollectionType type, byte[] value) { + Value val = Value.get(value); + return new Filter(FilterType.CONTAINS_BY_INDEX, indexName, null, type, val.getType(), val, val); + } + + /** + * Create range filter for query. + * Range arguments must be longs or integers which can be cast to longs. + * String ranges are not supported. + * + * @param name bin name + * @param begin filter begin value inclusive + * @param end filter end value inclusive + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter range(String name, long begin, long end, CTX... ctx) { + return new Filter(FilterType.RANGE, name, IndexCollectionType.DEFAULT, ParticleType.INTEGER, Value.get(begin), Value.get(end), ctx); + } + + /** + * Create a range filter for an Expression-based Secondary Index (SI) query, with the actual Expression that was + * used to create the SI. + * Range arguments must be longs or integers which can be cast to longs. + * String ranges are not supported. + * + * @param exp the Expression + * @param begin filter begin value inclusive + * @param end filter end value inclusive + * @return filter instance + */ + public static Filter range(Expression exp, long begin, long end) { + return new Filter(FilterType.RANGE, null, exp.getBytes(), IndexCollectionType.DEFAULT, ParticleType.INTEGER, + Value.get(begin), Value.get(end)); + } + + /** + * Create a range filter for an Expression-based Secondary Index (SI) query, using the SI name + * Range arguments must be longs or integers which can be cast to longs. + * String ranges are not supported. + * + * @param indexName the name that was assigned to the SI + * @param begin filter begin value inclusive + * @param end filter end value inclusive + * @return filter instance + */ + public static Filter rangeByIndex(String indexName, long begin, long end) { + return new Filter(FilterType.RANGE_BY_INDEX, indexName, null, IndexCollectionType.DEFAULT, ParticleType.INTEGER, + Value.get(begin), Value.get(end)); + } + + /** + * Create range filter for query on collection index. + * Range arguments must be longs or integers which can be cast to longs. + * String ranges are not supported. + * + * @param name bin name + * @param type index collection type + * @param begin filter begin value inclusive + * @param end filter end value inclusive + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter range(String name, IndexCollectionType type, long begin, long end, CTX... ctx) { + return new Filter(FilterType.RANGE, name, type, ParticleType.INTEGER, Value.get(begin), Value.get(end), ctx); + } + + /** + * Create range filter for query on collection index with expression. + * Range arguments must be longs or integers which can be cast to longs. + * String ranges are not supported. + * + * @param exp the Expression to use for the filter + * @param type index collection type + * @param begin filter begin value inclusive + * @param end filter end value inclusive + * @return filter instance + */ + public static Filter range(Expression exp, IndexCollectionType type, long begin, long end) { + return new Filter(FilterType.RANGE, null, exp.getBytes(), type, ParticleType.INTEGER, Value.get(begin), Value.get(end)); + } + + /** + * Create range filter for query on collection index with index name. + * Range arguments must be longs or integers which can be cast to longs. + * String ranges are not supported. + * + * @param indexName index name + * @param type index collection type + * @param begin filter begin value inclusive + * @param end filter end value inclusive + * @return filter instance + */ + public static Filter rangeByIndex(String indexName, IndexCollectionType type, long begin, long end) { + return new Filter(FilterType.RANGE_BY_INDEX, indexName, null, type, ParticleType.INTEGER, Value.get(begin), Value.get(end)); + } + + /** + * Create geospatial "within region" filter for query. + * + * @param name bin name + * @param region GeoJSON region + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter geoWithinRegion(String name, String region, CTX... ctx) { + return new Filter(FilterType.GEO_WITHIN_REGION, name, IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(region), Value.get(region), ctx); + } + + /** + * Create geospatial "within region" filter for query with expression. + * + * @param exp the Expression to use for the filter + * @param region GeoJSON region + * @return filter instance + */ + public static Filter geoWithinRegion(Expression exp, String region) { + return new Filter(FilterType.GEO_WITHIN_REGION, null, exp.getBytes(), IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(region), Value.get(region)); + } + + /** + * Create geospatial "within region" filter for query with index name. + * + * @param indexName index name + * @param region GeoJSON region + * @return filter instance + */ + public static Filter geoWithinRegionByIndex(String indexName, String region) { + return new Filter(FilterType.GEO_WITHIN_REGION_BY_INDEX, indexName, null, IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(region), Value.get(region)); + } + + /** + * Create geospatial "within region" filter for query on collection index. + * + * @param name bin name + * @param type index collection type + * @param region GeoJSON region + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter geoWithinRegion(String name, IndexCollectionType type, String region, CTX... ctx) { + return new Filter(FilterType.GEO_WITHIN_REGION, name, type, ParticleType.GEOJSON, Value.get(region), Value.get(region), ctx); + } + + /** + * Create geospatial "within region" filter for query on collection index with expression. + * + * @param exp the Expression to use for the filter + * @param type index collection type + * @param region GeoJSON region + * @return filter instance + */ + public static Filter geoWithinRegion(Expression exp, IndexCollectionType type, String region) { + return new Filter(FilterType.GEO_WITHIN_REGION, null, exp.getBytes(), type, ParticleType.GEOJSON, Value.get(region), Value.get(region)); + } + + /** + * Create geospatial "within region" filter for query on collection index with index name. + * + * @param indexName index name + * @param type index collection type + * @param region GeoJSON region + * @return filter instance + */ + public static Filter geoWithinRegionByIndex(String indexName, IndexCollectionType type, String region) { + return new Filter(FilterType.GEO_WITHIN_REGION_BY_INDEX, indexName, null, type, ParticleType.GEOJSON, Value.get(region), Value.get(region)); + } + + /** + * Create geospatial "within radius" filter for query. + * + * @param name bin name + * @param lng longitude + * @param lat latitude + * @param radius radius (meters) + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter geoWithinRadius(String name, double lng, double lat, double radius, CTX... ctx) { + String rgnstr = + String.format("{ \"type\": \"AeroCircle\", " + + "\"coordinates\": [[%.8f, %.8f], %f] }", + lng, lat, radius); + return new Filter(FilterType.GEO_WITHIN_RADIUS, name, IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(rgnstr), Value.get(rgnstr), ctx); + } + + /** + * Create geospatial "within radius" filter for query with expression. + * + * @param exp the Expression to use for the filter + * @param lng longitude + * @param lat latitude + * @param radius radius (meters) + * @return filter instance + */ + public static Filter geoWithinRadius(Expression exp, double lng, double lat, double radius) { + String rgnstr = + String.format("{ \"type\": \"AeroCircle\", " + + "\"coordinates\": [[%.8f, %.8f], %f] }", + lng, lat, radius); + return new Filter(FilterType.GEO_WITHIN_RADIUS, null, exp.getBytes(), IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(rgnstr), Value.get(rgnstr)); + } + + /** + * Create geospatial "within radius" filter for query with index name. + * + * @param indexName index name + * @param lng longitude + * @param lat latitude + * @param radius radius (meters) + * @return filter instance + */ + public static Filter geoWithinRadiusByIndex(String indexName, double lng, double lat, double radius) { + String rgnstr = + String.format("{ \"type\": \"AeroCircle\", " + + "\"coordinates\": [[%.8f, %.8f], %f] }", + lng, lat, radius); + return new Filter(FilterType.GEO_WITHIN_RADIUS_BY_INDEX, indexName, null, IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(rgnstr), Value.get(rgnstr)); + } + + /** + * Create geospatial "within radius" filter for query on collection index. + * + * @param name bin name + * @param type index collection type + * @param lng longitude + * @param lat latitude + * @param radius radius (meters) + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter geoWithinRadius(String name, IndexCollectionType type, double lng, double lat, double radius, CTX... ctx) { + String rgnstr = + String.format("{ \"type\": \"AeroCircle\", " + + "\"coordinates\": [[%.8f, %.8f], %f] }", + lng, lat, radius); + return new Filter(FilterType.GEO_WITHIN_RADIUS, name, type, ParticleType.GEOJSON, Value.get(rgnstr), Value.get(rgnstr), ctx); + } + + /** + * Create geospatial "within radius" filter for query on collection index with expression. + * + * @param exp the Expression to use for the filter + * @param type index collection type + * @param lng longitude + * @param lat latitude + * @param radius radius (meters) + * @return filter instance + */ + public static Filter geoWithinRadius(Expression exp, IndexCollectionType type, double lng, double lat, double radius) { + String rgnstr = + String.format("{ \"type\": \"AeroCircle\", " + + "\"coordinates\": [[%.8f, %.8f], %f] }", + lng, lat, radius); + return new Filter(FilterType.GEO_WITHIN_RADIUS, null, exp.getBytes(), type, ParticleType.GEOJSON, Value.get(rgnstr), Value.get(rgnstr)); + } + + /** + * Create geospatial "within radius" filter for query on collection index with index name. + * + * @param indexName index name + * @param type index collection type + * @param lng longitude + * @param lat latitude + * @param radius radius (meters) + * @return filter instance + */ + public static Filter geoWithinRadiusByIndex(String indexName, IndexCollectionType type, double lng, double lat, double radius) { + String rgnstr = + String.format("{ \"type\": \"AeroCircle\", " + + "\"coordinates\": [[%.8f, %.8f], %f] }", + lng, lat, radius); + return new Filter(FilterType.GEO_WITHIN_RADIUS_BY_INDEX, indexName, null, type, ParticleType.GEOJSON, Value.get(rgnstr), Value.get(rgnstr)); + } + + /** + * Create geospatial "containing point" filter for query. + * + * @param name bin name + * @param point GeoJSON point + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter geoContains(String name, String point, CTX... ctx) { + return new Filter(FilterType.GEO_CONTAINS, name, IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(point), Value.get(point), ctx); + } + + /** + * Create geospatial "containing point" filter for query with expression. + * + * @param exp the Expression to use for the filter + * @param point GeoJSON point + * @return filter instance + */ + public static Filter geoContains(Expression exp, String point) { + return new Filter(FilterType.GEO_CONTAINS, null, exp.getBytes(), IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(point), Value.get(point)); + } + + /** + * Create geospatial "containing point" filter for query with index name. + * + * @param indexName index name + * @param point GeoJSON point + * @return filter instance + */ + public static Filter geoContainsByIndex(String indexName, String point) { + return new Filter(FilterType.GEO_CONTAINS_BY_INDEX, indexName, null, IndexCollectionType.DEFAULT, ParticleType.GEOJSON, Value.get(point), Value.get(point)); + } + + /** + * Create geospatial "containing point" filter for query on collection index. + * + * @param name bin name + * @param type index collection type + * @param point GeoJSON point + * @param ctx optional context for elements within a CDT + * @return filter instance + */ + public static Filter geoContains(String name, IndexCollectionType type, String point, CTX... ctx) { + return new Filter(FilterType.GEO_CONTAINS, name, type, ParticleType.GEOJSON, Value.get(point), Value.get(point), ctx); + } + + /** + * Create geospatial "containing point" filter for query on collection index with expression. + * + * @param exp the Expression to use for the filter + * @param type index collection type + * @param point GeoJSON point + * @return filter instance + */ + public static Filter geoContains(Expression exp, IndexCollectionType type, String point) { + return new Filter(FilterType.GEO_CONTAINS, null, exp.getBytes(), type, ParticleType.GEOJSON, Value.get(point), Value.get(point)); + } + + /** + * Create geospatial "containing point" filter for query on collection index with index name. + * + * @param indexName index name + * @param type index collection type + * @param point GeoJSON point + * @return filter instance + */ + public static Filter geoContainsByIndex(String indexName, IndexCollectionType type, String point) { + return new Filter(FilterType.GEO_CONTAINS_BY_INDEX, indexName, null, type, ParticleType.GEOJSON, Value.get(point), Value.get(point)); + } + + private final String name; + private final String indexName; + private final IndexCollectionType colType; + private final byte[] packedCtx; + private final int valType; + private final Value begin; + private final Value end; + private final byte[] packedExp; + + private Filter(FilterType type, String name, IndexCollectionType colType, int valType, Value begin, Value end, + CTX[] ctx) { + this(type, name, null, colType, valType, begin, end, (ctx != null && ctx.length > 0) ? Pack.pack(ctx) : null, null); + } + private Filter(FilterType type, String indexName, byte[] exp, IndexCollectionType colType, int valType, Value begin, + Value end) { + this(type, null, indexName, colType, valType, begin, end, null, exp); + } + + Filter(FilterType filterType, String name, String indexName, IndexCollectionType colType, int valType, Value begin, Value end, + byte[] packedCtx, byte[] packedExp) { + this.filterType = filterType; + this.name = name; + this.indexName = indexName; + this.colType = colType; + this.valType = valType; + this.begin = begin; + this.end = end; + this.packedCtx = packedCtx; + this.packedExp = packedExp; + } + + /** + * Write filter to send command buffer. + * For internal use only. + */ + public int write(byte[] buf, int offset) { + // Write name. + int len = Buffer.stringToUtf8(name, buf, offset + 1); + buf[offset] = (byte)len; + offset += len + 1; + + // Write particle type. + buf[offset++] = (byte)valType; + + // Write filter begin. + len = begin.write(buf, offset + 4); + Buffer.intToBytes(len, buf, offset); + offset += len + 4; + + // Write filter end. + len = end.write(buf, offset + 4); + Buffer.intToBytes(len, buf, offset); + offset += len + 4; + + return offset; + } + + /** + * Retrieve index collection type. + * For internal use only. + */ + public IndexCollectionType getCollectionType() { + return colType; + } + + /** + * Filter name. + * For internal use only. + */ + public String getName() { + return name; + } + + /** + * Index name. + * For internal use only. + */ + public String getIndexName() { + return indexName; + } + + /** + * Index collection type. + * For internal use only. + */ + public IndexCollectionType getColType() { + return colType; + } + + /** + * Filter begin value. + * For internal use only. + */ + public Value getBegin() { + return begin; + } + + /** + * Filter begin value. + * For internal use only. + */ + public Value getEnd() { + return end; + } + + /** + * Filter Value type. + * For internal use only. + */ + public int getValType() { + return valType; + } + + /** + * Retrieve packed Context. + * For internal use only. + */ + public byte[] getPackedCtx() { + return packedCtx; + } + + /** + * Retrieve packed Expression. + * For internal use only. + */ + public byte[] getPackedExp() { + return packedExp; + } + + /** + * Check for Filter equality. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Filter other = (Filter) obj; + if (begin == null) { + if (other.begin != null) + return false; + } else if (!begin.equals(other.begin)) + return false; + if (colType != other.colType) + return false; + if (end == null) { + if (other.end != null) + return false; + } else if (!end.equals(other.end)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (!Arrays.equals(packedCtx, other.packedCtx)) + return false; + if (valType != other.valType) + return false; + return true; + } + + /** + * Generate Filter hashCode. + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((begin == null) ? 0 : begin.hashCode()); + result = prime * result + ((colType == null) ? 0 : colType.hashCode()); + result = prime * result + ((end == null) ? 0 : end.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + Arrays.hashCode(packedCtx); + result = prime * result + valType; + return result; + } +} diff --git a/src/main/java/com/aerospike/dsl/client/query/IndexCollectionType.java b/src/main/java/com/aerospike/dsl/client/query/IndexCollectionType.java new file mode 100644 index 0000000..54b01b6 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/query/IndexCollectionType.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2021 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.query; + +/** + * Secondary index collection type. + */ +public enum IndexCollectionType { + /** + * Normal scalar index. + */ + DEFAULT, + + /** + * Index list elements. + */ + LIST, + + /** + * Index map keys. + */ + MAPKEYS, + + /** + * Index map values. + */ + MAPVALUES; +} diff --git a/src/main/java/com/aerospike/dsl/client/query/IndexType.java b/src/main/java/com/aerospike/dsl/client/query/IndexType.java new file mode 100644 index 0000000..c9c2c35 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/query/IndexType.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.query; + +/** + * Underlying data type of secondary index. + */ +public enum IndexType { + /** + * Number index. + */ + NUMERIC, + + /** + * String index. + */ + STRING, + + /** + * byte[] index. Requires server version 7.0+. + */ + BLOB, + + /** + * 2-dimensional spherical geospatial index. + */ + GEO2DSPHERE; +} diff --git a/src/main/java/com/aerospike/dsl/client/query/RegexFlag.java b/src/main/java/com/aerospike/dsl/client/query/RegexFlag.java new file mode 100644 index 0000000..15d4031 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/query/RegexFlag.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.query; + +/** + * Regex bit flags. Use BITWISE OR to combine flags. Example: + * + *
{@code
+ * int flags = RegexFlag.ICASE | RegexFlag.NEWLINE;
+ * }
+ */ +public final class RegexFlag { + /** + * Use regex defaults. + */ + public static final int NONE = 0; + + /** + * Use POSIX Extended Regular Expression syntax when interpreting regex. + */ + public static final int EXTENDED = 1; + + /** + * Do not differentiate case. + */ + public static final int ICASE = 2; + + /** + * Do not report position of matches. + */ + public static final int NOSUB = 4; + + /** + * Match-any-character operators don't match a newline. + */ + public static final int NEWLINE = 8; +} diff --git a/src/main/java/com/aerospike/dsl/client/util/Crypto.java b/src/main/java/com/aerospike/dsl/client/util/Crypto.java new file mode 100644 index 0000000..2ffdbe0 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/util/Crypto.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2024 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.util; + +import gnu.crypto.util.Base64; + +public final class Crypto { + + /** + * Decode base64 bytes into a byte array. + */ + public static byte[] decodeBase64(byte[] src, int off, int len) { + return Base64.decode(src, off, len); + } + + /** + * Encode bytes into a base64 encoded string. + */ + public static String encodeBase64(byte[] src) { + return Base64.encode(src, 0, src.length, false); + } +} diff --git a/src/main/java/com/aerospike/dsl/client/util/Pack.java b/src/main/java/com/aerospike/dsl/client/util/Pack.java new file mode 100644 index 0000000..ca7547a --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/util/Pack.java @@ -0,0 +1,380 @@ +/* + * Copyright 2012-2022 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.util; + +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; + +import java.util.List; + +public final class Pack { + public static byte[] pack(int command, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(1); + packer.packInt(command); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(2); + packer.packInt(command); + packer.packInt(v1); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, int v2, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(3); + packer.packInt(command); + packer.packInt(v1); + packer.packInt(v2); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, int v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + packer.packInt(v1); + packer.packInt(v2); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, int v2, int v3, int v4, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + packer.packInt(v1); + packer.packInt(v2); + packer.packInt(v3); + packer.packInt(v4); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, int v2, long v3, int v4, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + packer.packInt(v1); + packer.packInt(v2); + packer.packLong(v3); + packer.packInt(v4); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, int v2, boolean v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + packer.packInt(v1); + packer.packInt(v2); + packer.packBoolean(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, int v2, byte[] v3, int v4, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + packer.packInt(v1); + packer.packInt(v2); + packer.packParticleBytes(v3); + packer.packInt(v4); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, byte[] v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + packer.packInt(v1); + packer.packParticleBytes(v2); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, Value v2, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(3); + packer.packInt(command); + packer.packInt(v1); + v2.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, Value v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + packer.packInt(v1); + v2.pack(packer); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, Value v2, int v3, int v4, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + packer.packInt(v1); + v2.pack(packer); + packer.packInt(v3); + packer.packInt(v4); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, List list, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(3); + packer.packInt(command); + packer.packInt(v1); + packer.packValueList(list); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, List v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + packer.packInt(v1); + packer.packValueList(v2); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Value value, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(2); + packer.packInt(command); + value.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Value value, int v1, int v2, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + value.pack(packer); + packer.packInt(v1); + packer.packInt(v2); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Value v1, Value v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + v1.pack(packer); + v2.pack(packer); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, List list, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(2); + packer.packInt(command); + packer.packList(list); + return packer.toByteArray(); + } + + public static byte[] pack(int command, List list, int v1, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(3); + packer.packInt(command); + packer.packList(list); + packer.packInt(v1); + return packer.toByteArray(); + } + + public static byte[] pack(int command, List list, int v1, int v2, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + packer.packValueList(list); + packer.packInt(v1); + packer.packInt(v2); + return packer.toByteArray(); + } + + public static byte[] pack(int command, List list, int v1, int v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + packer.packValueList(list); + packer.packInt(v1); + packer.packInt(v2); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, Exp v2, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(3); + packer.packInt(command); + packer.packInt(v1); + v2.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, Exp v2, Exp v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + packer.packInt(v1); + v2.pack(packer); + v3.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, int v1, Exp v2, Exp v3, Exp v4, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + packer.packInt(v1); + v2.pack(packer); + v3.pack(packer); + v4.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Exp v1) { + Packer packer = new Packer(); + packer.packArrayBegin(2); + packer.packInt(command); + v1.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Exp v1, int v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + v1.pack(packer); + packer.packInt(v2); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Exp v1, Exp v2, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(3); + packer.packInt(command); + v1.pack(packer); + v2.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Exp v1, Exp v2, int v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + v1.pack(packer); + v2.pack(packer); + packer.packInt(v3); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Exp v1, Exp v2, int v3, int v4, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + v1.pack(packer); + v2.pack(packer); + packer.packInt(v3); + packer.packInt(v4); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Exp v1, Exp v2, Exp v3, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(4); + packer.packInt(command); + v1.pack(packer); + v2.pack(packer); + v3.pack(packer); + return packer.toByteArray(); + } + + public static byte[] pack(int command, Exp v1, Exp v2, Exp v3, int v4, CTX... ctx) { + Packer packer = new Packer(); + init(packer, ctx); + packer.packArrayBegin(5); + packer.packInt(command); + v1.pack(packer); + v2.pack(packer); + v3.pack(packer); + packer.packInt(v4); + return packer.toByteArray(); + } + + public static void init(Packer packer, CTX[] ctx) { + if (ctx != null && ctx.length > 0) { + packer.packArrayBegin(3); + packer.packInt(0xff); + packer.packArrayBegin(ctx.length * 2); + + for (CTX c : ctx) { + packer.packInt(c.id); + c.value.pack(packer); + } + } + } + + public static byte[] pack(CTX[] ctx) { + Packer packer = new Packer(); + packer.packArrayBegin(ctx.length * 2); + + for (CTX c : ctx) { + packer.packInt(c.id); + c.value.pack(packer); + } + return packer.toByteArray(); + } +} diff --git a/src/main/java/com/aerospike/dsl/client/util/Packer.java b/src/main/java/com/aerospike/dsl/client/util/Packer.java new file mode 100644 index 0000000..ccf4c66 --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/util/Packer.java @@ -0,0 +1,607 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.util; + +import com.aerospike.dsl.client.AerospikeException; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.MapOrder; +import com.aerospike.dsl.client.command.Buffer; +import com.aerospike.dsl.client.command.ParticleType; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +import static com.aerospike.dsl.client.Value.MapValue.getMapOrder; + +/** + * Serialize collection objects using MessagePack format specification: + * + * https://github.com/msgpack/msgpack/blob/master/spec.md + */ +public final class Packer { + + public static byte[] pack(Value[] val) { + try { + Packer packer = new Packer(); + packer.packValueArray(val); + return packer.toByteArray(); + } + catch (Throwable e) { + throw new AerospikeException.Serialize(e); + } + } + + public static byte[] pack(List val) { + try { + Packer packer = new Packer(); + packer.packList(val); + return packer.toByteArray(); + } + catch (Throwable e) { + throw new AerospikeException.Serialize(e); + } + } + + public static byte[] pack(Map val, MapOrder order) { + try { + Packer packer = new Packer(); + packer.packMap(val, order); + return packer.toByteArray(); + } + catch (Throwable e) { + throw new AerospikeException.Serialize(e); + } + } + + public static byte[] pack(List> val, MapOrder order) { + try { + Packer packer = new Packer(); + packer.packMap(val, order); + return packer.toByteArray(); + } + catch (Throwable e) { + throw new AerospikeException.Serialize(e); + } + } + + private byte[] buffer; + private int offset; + private ArrayList bufferList; + + public Packer() { + this.buffer = ThreadLocalData.getBuffer(); + } + + public void packValueArray(Value[] values) { + packArrayBegin(values.length); + for (Value value : values) { + value.pack(this); + } + } + + public void packValueList(List list) { + packArrayBegin(list.size()); + for (Value value : list) { + value.pack(this); + } + } + + public void packList(List list) { + packArrayBegin(list.size()); + for (Object obj : list) { + packObject(obj); + } + } + + public void packArrayBegin(int size) { + if (size < 16) { + packByte(0x90 | size); + } + else if (size < 65536) { + packShort(0xdc, size); + } + else { + packInt(0xdd, size); + } + } + + public void packValueMap(Map map) { + MapOrder order = getMapOrder(map); + packMapBegin(map.size(), order); + + for (Entry entry : map.entrySet()) { + entry.getKey().pack(this); + entry.getValue().pack(this); + } + } + + public void packMap(Map map) { + MapOrder order = getMapOrder(map); + packMap(map, order); + } + + public void packMap(Map map, MapOrder order) { + packMapBegin(map.size(), order); + + for (Entry entry : map.entrySet()) { + packObject(entry.getKey()); + packObject(entry.getValue()); + } + } + + public void packMap(List> list, MapOrder order) { + packMapBegin(list.size(), order); + + for (Entry entry : list) { + packObject(entry.getKey()); + packObject(entry.getValue()); + } + } + + public void packMapBegin(int size, MapOrder order) { + if (order == MapOrder.UNORDERED) { + packMapBegin(size); + } + else { + // Map is sorted. + packMapBegin(size + 1); + packByte(0xc7); + packByte(0); + packByte(order.attributes); + packByte(0xc0); + } + } + + public void packMapBegin(int size) { + if (size < 16) { + packByte(0x80 | size); + } + else if (size < 65536) { + packShort(0xde, size); + } + else { + packInt(0xdf, size); + } + } + + public void packBytes(byte[] b) { + packByteArrayBegin(b.length); + packByteArray(b, 0, b.length); + } + + public void packParticleBytes(byte[] b) { + packByteArrayBegin(b.length + 1); + packByte(ParticleType.BLOB); + packByteArray(b, 0, b.length); + } + + public void packParticleBytes(byte[] b, int type) { + packByteArrayBegin(b.length + 1); + packByte(type); + packByteArray(b, 0, b.length); + } + + public void packParticleBytes(byte[] b, int offset, int length) { + packByteArrayBegin(length + 1); + packByte(ParticleType.BLOB); + packByteArray(b, offset, length); + } + + public void packGeoJSON(String val) { + byte[] buffer = Buffer.stringToUtf8(val); + packByteArrayBegin(buffer.length + 1); + packByte(ParticleType.GEOJSON); + packByteArray(buffer, 0, buffer.length); + } + + private void packByteArrayBegin(int size) { + // Use string header codes for byte arrays. + packStringBegin(size); + /* + if (size < 256) { + packByte(0xc4, size); + } + else if (size < 65536) { + packShort(0xc5, size); + } + else { + packInt(0xc6, size); + } + */ + } + + public void packObject(Object obj) { + if (obj == null) { + packNil(); + return; + } + + if (obj instanceof Value) { + Value value = (Value)obj; + value.pack(this); + return; + } + + if (obj instanceof byte[]) { + packParticleBytes((byte[])obj); + return; + } + + if (obj instanceof String) { + packParticleString((String)obj); + return; + } + + if (obj instanceof Integer) { + packInt((Integer)obj); + return; + } + + if (obj instanceof Long) { + packLong((Long)obj); + return; + } + + if (obj instanceof List) { + packList((List)obj); + return; + } + + if (obj instanceof Map) { + packMap((Map)obj); + return; + } + + if (obj instanceof Double) { + packDouble((Double)obj); + return; + } + + if (obj instanceof Float) { + packFloat((Float)obj); + return; + } + + if (obj instanceof Short) { + packInt((Short)obj); + return; + } + + if (obj instanceof Boolean) { + packBoolean((Boolean)obj); + return; + } + + if (obj instanceof Byte) { + packInt(((Byte)obj) & 0xff); + return; + } + + if (obj instanceof Character) { + packInt(((Character)obj).charValue()); + return; + } + + if (obj instanceof Enum) { + packString(obj.toString()); + return; + } + + if (obj instanceof UUID) { + packString(obj.toString()); + return; + } + + if (obj instanceof ByteBuffer) { + packByteBuffer((ByteBuffer) obj); + return; + } + + throw new AerospikeException("Unsupported type: " + obj.getClass().getName()); + } + + public void packByteBuffer(ByteBuffer bb) { + byte[] b = bb.array(); + packParticleBytes(b); + } + + public void packLong(long val) { + if (val >= 0L) { + if (val < 128L) { + packByte((int)val); + return; + } + + if (val < 256L) { + packByte(0xcc, (int)val); + return; + } + + if (val < 65536L) { + packShort(0xcd, (int)val); + return; + } + + if (val < 4294967296L) { + packInt(0xce, (int)val); + return; + } + packLong(0xcf, val); + } + else { + if (val >= -32) { + packByte(0xe0 | ((int)val + 32)); + return; + } + + if (val >= Byte.MIN_VALUE) { + packByte(0xd0, (int)val); + return; + } + + if (val >= Short.MIN_VALUE) { + packShort(0xd1, (int)val); + return; + } + + if (val >= Integer.MIN_VALUE) { + packInt(0xd2, (int)val); + return; + } + packLong(0xd3, val); + } + } + + public void packInt(int val) { + if (val >= 0) { + if (val < 128) { + packByte(val); + return; + } + + if (val < 256) { + packByte(0xcc, val); + return; + } + + if (val < 65536) { + packShort(0xcd, val); + return; + } + packInt(0xce, val); + } + else { + if (val >= -32) { + packByte(0xe0 | (val + 32)); + return; + } + + if (val >= Byte.MIN_VALUE) { + packByte(0xd0, val); + return; + } + + if (val >= Short.MIN_VALUE) { + packShort(0xd1, val); + return; + } + packInt(0xd2, val); + } + } + + public void packString(String val) { + int size = Buffer.estimateSizeUtf8(val); + packStringBegin(size); + + if (offset + size > buffer.length) { + resize(size); + } + offset += Buffer.stringToUtf8(val, buffer, offset); + } + + public void packParticleString(String val) { + int size = Buffer.estimateSizeUtf8(val) + 1; + packStringBegin(size); + + if (offset + size > buffer.length) { + resize(size); + } + buffer[offset++] = (byte)ParticleType.STRING; + offset += Buffer.stringToUtf8(val, buffer, offset); + } + + private void packStringBegin(int size) { + if (size < 32) { + packByte(0xa0 | size); + } + else if (size < 256) { + packByte(0xd9, size); + } + else if (size < 65536) { + packShort(0xda, size); + } + else { + packInt(0xdb, size); + } + } + + public void packByteArray(byte[] src, int srcOffset, int srcLength) { + if (offset + srcLength > buffer.length) { + resize(srcLength); + } + System.arraycopy(src, srcOffset, buffer, offset, srcLength); + offset += srcLength; + } + + public void packDouble(double val) { + if (offset + 9 > buffer.length) { + resize(9); + } + buffer[offset++] = (byte)0xcb; + Buffer.longToBytes(Double.doubleToLongBits(val), buffer, offset); + offset += 8; + } + + public void packFloat(float val) { + if (offset + 5 > buffer.length) { + resize(5); + } + buffer[offset++] = (byte)0xca; + Buffer.intToBytes(Float.floatToIntBits(val), buffer, offset); + offset += 4; + } + + private void packLong(int type, long val) { + if (offset + 9 > buffer.length) { + resize(9); + } + buffer[offset++] = (byte)type; + Buffer.longToBytes(val, buffer, offset); + offset += 8; + } + + private void packInt(int type, int val) { + if (offset + 5 > buffer.length) { + resize(5); + } + buffer[offset++] = (byte)type; + Buffer.intToBytes(val, buffer, offset); + offset += 4; + } + + private void packShort(int type, int val) { + if (offset + 3 > buffer.length) { + resize(3); + } + buffer[offset++] = (byte)type; + Buffer.shortToBytes(val, buffer, offset); + offset += 2; + } + + public void packRawShort(int val) { + // WARNING. This method is not compatible with message pack standard. + if (offset + 2 > buffer.length) { + resize(2); + } + Buffer.shortToBytes(val, buffer, offset); + offset += 2; + } + + private void packByte(int type, int val) { + if (offset + 2 > buffer.length) { + resize(2); + } + buffer[offset++] = (byte)type; + buffer[offset++] = (byte)val; + } + + public void packBoolean(boolean val) { + if (offset + 1 > buffer.length) { + resize(1); + } + + if (val) { + buffer[offset++] = (byte)0xc3; + } + else { + buffer[offset++] = (byte)0xc2; + } + } + + public void packNil() { + if (offset >= buffer.length) { + resize(1); + } + buffer[offset++] = (byte)0xc0; + } + + public void packInfinity() { + if (offset + 3 > buffer.length) { + resize(3); + } + buffer[offset++] = (byte)0xd4; + buffer[offset++] = (byte)0xff; + buffer[offset++] = (byte)0x01; + } + + public void packWildcard() { + if (offset + 3 > buffer.length) { + resize(3); + } + buffer[offset++] = (byte)0xd4; + buffer[offset++] = (byte)0xff; + buffer[offset++] = (byte)0x00; + } + + public void packByte(int val) { + if (offset >= buffer.length) { + resize(1); + } + buffer[offset++] = (byte)val; + } + + private void resize(int size) { + if (bufferList == null) { + bufferList = new ArrayList(); + } + bufferList.add(new BufferItem(buffer, offset)); + + if (size < buffer.length) { + size = buffer.length; + } + buffer = new byte[size]; + offset = 0; + } + + public byte[] toByteArray() { + if (bufferList != null) { + int size = offset; + for (BufferItem item : bufferList) { + size += item.length; + } + + byte[] target = new byte[size]; + size = 0; + for (BufferItem item : bufferList) { + System.arraycopy(item.buffer, 0, target, size, item.length); + size += item.length; + } + + System.arraycopy(buffer, 0, target, size, offset); + return target; + } + else { + byte[] target = new byte[offset]; + System.arraycopy(buffer, 0, target, 0, offset); + return target; + } + } + + private static final class BufferItem { + private final byte[] buffer; + private final int length; + + private BufferItem(byte[] buffer, int length) { + this.buffer = buffer; + this.length = length; + } + } +} diff --git a/src/main/java/com/aerospike/dsl/client/util/ThreadLocalData.java b/src/main/java/com/aerospike/dsl/client/util/ThreadLocalData.java new file mode 100644 index 0000000..4c2360c --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/util/ThreadLocalData.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.util; + +import lombok.extern.slf4j.Slf4j; + +/** + * Thread local buffer storage. + */ +@Slf4j +public final class ThreadLocalData { + /** + * Initial buffer size on first use of thread local buffer. + */ + public static int DefaultBufferSize = 8192; + + private static final int THREAD_LOCAL_CUTOFF = 1024 * 128; // 128 KB + //private static final int MAX_BUFFER_SIZE = 1024 * 1024; // 1 MB + + private static final ThreadLocal BufferThreadLocal = new ThreadLocal() { + @Override protected byte[] initialValue() { + return new byte[DefaultBufferSize]; + } + }; + + /** + * Return thread local buffer. + */ + public static byte[] getBuffer() { + return BufferThreadLocal.get(); + } + + /** + * Resize and return thread local buffer if the requested size <= 128 KB. + * Otherwise, the thread local buffer will not be resized and a new + * buffer will be returned from heap memory. + *

+ * This method should only be called when the current buffer is too small to + * hold the desired data. + */ + public static byte[] resizeBuffer(int size) { + // Do not store extremely large buffers in thread local storage. + if (size > THREAD_LOCAL_CUTOFF) { + /* + if (size > MAX_BUFFER_SIZE) { + throw new IllegalArgumentException("Thread " + Thread.currentThread().getId() + " invalid buffer size: " + size); + }*/ + + log.debug("Thread " + Thread.currentThread().getName() + " allocate buffer on heap " + size); + return new byte[size]; + } + + log.debug("Thread " + Thread.currentThread().getName() + " resize buffer to " + size); + BufferThreadLocal.set(new byte[size]); + return BufferThreadLocal.get(); + } +} diff --git a/src/main/java/com/aerospike/dsl/client/util/Unpacker.java b/src/main/java/com/aerospike/dsl/client/util/Unpacker.java new file mode 100644 index 0000000..f3e99ea --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/util/Unpacker.java @@ -0,0 +1,518 @@ +/* + * Copyright 2012-2023 Aerospike, Inc. + * + * Portions may be licensed to Aerospike, Inc. under one or more contributor + * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. + * + * Licensed 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 com.aerospike.dsl.client.util; + +import com.aerospike.dsl.client.AerospikeException; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.command.Buffer; +import com.aerospike.dsl.client.command.ParticleType; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +/** + * De-serialize collection objects using MessagePack format specification: + * + * https://github.com/msgpack/msgpack/blob/master/spec.md + */ +public abstract class Unpacker { + + private final byte[] buffer; + private int offset; + private final int length; + + public Unpacker(byte[] buffer, int offset, int length) { + this.buffer = buffer; + this.offset = offset; + this.length = length; + } + + public final T unpackList() throws AerospikeException { + if (length <= 0) { + return getList(new ArrayList(0)); + } + + try { + int type = buffer[offset++] & 0xff; + int count; + + if ((type & 0xf0) == 0x90) { + count = type & 0x0f; + } + else if (type == 0xdc) { + count = Buffer.bytesToShort(buffer, offset); + offset += 2; + } + else if (type == 0xdd) { + count = Buffer.bytesToInt(buffer, offset); + offset += 4; + } + else { + return getList(new ArrayList(0)); + } + return unpackList(count); + } + catch (Throwable e) { + throw new AerospikeException.Serialize(e); + } + } + + private T unpackList(int count) throws IOException, ClassNotFoundException { + if (count <= 0) { + return getList(new ArrayList(0)); + } + + // Extract first object. + int mark = offset; + int size = count; + T val = unpackObject(); + + if (val == null) { + // Determine if null value is because of an extension type. + int type = buffer[mark] & 0xff; + + if (type != 0xc0) { // not nil type + // Ignore extension type. + size--; + } + } + + ArrayList out = new ArrayList(size); + + if (size == count) { + out.add(val); + } + + for (int i = 1; i < count; i++) { + out.add(unpackObject()); + } + return getList(out); + } + + public final T unpackMap() throws AerospikeException { + if (length <= 0) { + return getMap(new HashMap(0)); + } + + try { + int type = buffer[offset++] & 0xff; + int count; + + if ((type & 0xf0) == 0x80) { + count = type & 0x0f; + } + else if (type == 0xde) { + count = Buffer.bytesToShort(buffer, offset); + offset += 2; + } + else if (type == 0xdf) { + count = Buffer.bytesToInt(buffer, offset); + offset += 4; + } + else { + return getMap(new HashMap(0)); + } + return unpackMap(count); + } + catch (Throwable e) { + throw new AerospikeException.Serialize(e); + } + } + + private T unpackMap(int count) throws IOException, ClassNotFoundException { + if (count <= 0) { + return getMap(new HashMap(0)); + } + + // Peek at buffer to determine map type, but do not advance. + int type = buffer[offset] & 0xff; + + // Check for extension that the server uses. + if (type == 0xc7) { + int extensionType = buffer[offset + 1] & 0xff; + + if (extensionType == 0) { + int mapBits = buffer[offset + 2] & 0xff; + + // Extension is a map type. Determine which one. + if ((mapBits & 0x08) != 0 && !Value.ReturnMapForKeyValue) { + // Index/rank range result where order needs to be preserved. + return unpackMapAsList(count); + } + else if ((mapBits & 0x01) != 0) { + // Sorted map + return unpackTreeMap(count); + } + } + } + return unpackHashMap(count); + } + + private T unpackHashMap(int count) throws IOException, ClassNotFoundException { + HashMap map = new HashMap(count); + + for (int i = 0; i < count; i++) { + T key = unpackObject(); + T val = unpackObject(); + + if (key != null) { + map.put(key, val); + } + } + return getMap(map); + } + + @SuppressWarnings("unchecked") + private T unpackTreeMap(int count) throws IOException, ClassNotFoundException { + TreeMap map = new TreeMap(); + + for (int i = 0; i < count; i++) { + T key = unpackObject(); + T val = unpackObject(); + + if (key != null) { + if (key instanceof byte[]) { + // TreeMap does not support byte[] keys, + // so wrap byte[] in a ByteBuffer. + key = (T)ByteBuffer.wrap((byte[])key); + } + map.put(key, val); + } + } + return getMap(map); + } + + @SuppressWarnings("unchecked") + private T unpackMapAsList(int count) throws IOException, ClassNotFoundException { + // Index/rank range result where order needs to be preserved. + // Store in List to preserve order. + // The first entry is going to be null (ignored), so use "count - 1" size. + List> list = new ArrayList>(count - 1); + + for (int i = 0; i < count; i++) { + T key = unpackObject(); + T val = unpackObject(); + + if (key != null) { + list.add(new AbstractMap.SimpleEntry(key, val)); + } + } + return getList((List)list); + } + + private T unpackBlob(int count) throws IOException, ClassNotFoundException { + int type = buffer[offset++] & 0xff; + count--; + T val; + + switch (type) { + case ParticleType.STRING: + val = getString(Buffer.utf8ToString(buffer, offset, count)); + break; + + case ParticleType.GEOJSON: + val = getGeoJSON(Buffer.utf8ToString(buffer, offset, count)); + break; + + case ParticleType.JBLOB: + // Java deserialization is no longer allowed, so return java serialized blob as byte[]. + val = getBlob(Arrays.copyOfRange(buffer, offset, offset + count)); + break; + + default: + val = getBlob(Arrays.copyOfRange(buffer, offset, offset + count)); + break; + } + offset += count; + return val; + } + + public T unpackObject() throws IOException, ClassNotFoundException { + int type = buffer[offset++] & 0xff; + + switch (type) { + case 0xc0: { // nil + return null; + } + + case 0xc3: { // boolean true + return getBoolean(true); + } + + case 0xc2: { // boolean false + return getBoolean(false); + } + + case 0xca: { // float + float val = Float.intBitsToFloat(Buffer.bytesToInt(buffer, offset)); + offset += 4; + return getDouble(val); + } + + case 0xcb: { // double + double val = Double.longBitsToDouble(Buffer.bytesToLong(buffer, offset)); + offset += 8; + return getDouble(val); + } + + case 0xd0: { // signed 8 bit integer + return getLong(buffer[offset++]); + } + + case 0xcc: { // unsigned 8 bit integer + return getLong(buffer[offset++] & 0xff); + } + + case 0xd1: { // signed 16 bit integer + short val = Buffer.bigSigned16ToShort(buffer, offset); + offset += 2; + return getLong(val); + } + + case 0xcd: { // unsigned 16 bit integer + int val = Buffer.bytesToShort(buffer, offset); + offset += 2; + return getLong(val); + } + + case 0xd2: { // signed 32 bit integer + int val = Buffer.bytesToInt(buffer, offset); + offset += 4; + return getLong(val); + } + + case 0xce: { // unsigned 32 bit integer + long val = Buffer.bigUnsigned32ToLong(buffer, offset); + offset += 4; + return getLong(val); + } + + case 0xd3: { // signed 64 bit integer + long val = Buffer.bytesToLong(buffer, offset); + offset += 8; + return getLong(val); + } + + case 0xcf: { // unsigned 64 bit integer + // Java is constrained to signed longs, so that is the best we can do here. + long val = Buffer.bytesToLong(buffer, offset); + offset += 8; + return getLong(val); + } + + case 0xc4: + case 0xd9: { // string/raw bytes with 8 bit header + int count = buffer[offset++] & 0xff; + return (T)unpackBlob(count); + } + + case 0xc5: + case 0xda: { // string/raw bytes with 16 bit header + int count = Buffer.bytesToShort(buffer, offset); + offset += 2; + return (T)unpackBlob(count); + } + + case 0xc6: + case 0xdb: { // string/raw bytes with 32 bit header + // Java array length is restricted to positive int values (0 - Integer.MAX_VALUE). + int count = Buffer.bytesToInt(buffer, offset); + offset += 4; + return (T)unpackBlob(count); + } + + case 0xdc: { // list with 16 bit header + int count = Buffer.bytesToShort(buffer, offset); + offset += 2; + return unpackList(count); + } + + case 0xdd: { // list with 32 bit header + // Java array length is restricted to positive int values (0 - Integer.MAX_VALUE). + int count = Buffer.bytesToInt(buffer, offset); + offset += 4; + return unpackList(count); + } + + case 0xde: { // map with 16 bit header + int count = Buffer.bytesToShort(buffer, offset); + offset += 2; + return unpackMap(count); + } + + case 0xdf: { // map with 32 bit header + // Java array length is restricted to positive int values (0 - Integer.MAX_VALUE). + int count = Buffer.bytesToInt(buffer, offset); + offset += 4; + return unpackMap(count); + } + + case 0xd4: { // Skip over type extension with 1 byte + offset += 1 + 1; + return null; + } + + case 0xd5: { // Skip over type extension with 2 bytes + offset += 1 + 2; + return null; + } + + case 0xd6: { // Skip over type extension with 4 bytes + offset += 1 + 4; + return null; + } + + case 0xd7: { // Skip over type extension with 8 bytes + offset += 1 + 8; + return null; + } + + case 0xd8: { // Skip over type extension with 16 bytes + offset += 1 + 16; + return null; + } + + case 0xc7: { // Skip over type extension with 8 bit header and bytes + int count = buffer[offset] & 0xff; + offset += count + 1 + 1; + return null; + } + + case 0xc8: { // Skip over type extension with 16 bit header and bytes + int count = Buffer.bytesToShort(buffer, offset); + offset += count + 1 + 2; + return null; + } + + case 0xc9: { // Skip over type extension with 32 bit header and bytes + int count = Buffer.bytesToInt(buffer, offset); + offset += count + 1 + 4; + return null; + } + + default: { + if ((type & 0xe0) == 0xa0) { // raw bytes with 8 bit combined header + return unpackBlob(type & 0x1f); + } + + if ((type & 0xf0) == 0x80) { // map with 8 bit combined header + return unpackMap(type & 0x0f); + } + + if ((type & 0xf0) == 0x90) { // list with 8 bit combined header + return unpackList(type & 0x0f); + } + + if (type < 0x80) { // 8 bit combined unsigned integer + return getLong(type); + } + + if (type >= 0xe0) { // 8 bit combined signed integer + return getLong(type - 0xe0 - 32); + } + throw new IOException("Unknown unpack type: " + type); + } + } + } + + protected abstract T getMap(Map value); + protected abstract T getList(List value); + protected abstract T getBlob(byte[] value); + protected abstract T getString(String value); + protected abstract T getLong(long value); + protected abstract T getDouble(double value); + protected abstract T getBoolean(boolean value); + protected abstract T getGeoJSON(String value); + + public static Object unpackObjectList(byte[] buffer, int offset, int length) throws AerospikeException { + ObjectUnpacker unpacker = new ObjectUnpacker(buffer, offset, length); + return unpacker.unpackList(); + } + + public static Object unpackObjectMap(byte[] buffer, int offset, int length) throws AerospikeException { + ObjectUnpacker unpacker = new ObjectUnpacker(buffer, offset, length); + return unpacker.unpackMap(); + } + + public static Object unpackObject(byte[] buffer, int offset, int length) throws AerospikeException { + try { + if (length <= 0) { + return null; + } + ObjectUnpacker unpacker = new ObjectUnpacker(buffer, offset, length); + return unpacker.unpackObject(); + } + catch (Throwable e) { + throw new AerospikeException.Serialize(e); + } + } + + public static final class ObjectUnpacker extends Unpacker { + + public ObjectUnpacker(byte[] buffer, int offset, int length) { + super(buffer, offset, length); + } + + @Override + protected Object getMap(Map value) { + return value; + } + + @Override + protected Object getList(List value) { + return value; + } + + @Override + protected Object getBlob(byte[] value) { + return value; + } + + @Override + protected Object getString(String value) { + return value; + } + + @Override + protected Object getLong(long value) { + return value; + } + + @Override + protected Object getDouble(double value) { + return value; + } + + @Override + protected Object getBoolean(boolean value) { + return value; + } + + @Override + protected Object getGeoJSON(String value) { + return Value.getAsGeoJSON(value); + } + } +} diff --git a/src/main/java/com/aerospike/dsl/client/util/Utf8.java b/src/main/java/com/aerospike/dsl/client/util/Utf8.java new file mode 100644 index 0000000..42af47e --- /dev/null +++ b/src/main/java/com/aerospike/dsl/client/util/Utf8.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2013 The Guava Authors + * + * Licensed 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 com.aerospike.dsl.client.util; + +import com.aerospike.dsl.client.AerospikeException; + +import static java.lang.Character.MAX_SURROGATE; +import static java.lang.Character.MIN_SURROGATE; + +/** + * Low-level, high-performance utility methods related to the UTF-8 + * character encoding. UTF-8 is defined in section D92 of The Unicode Standard Core + * Specification, Chapter 3. + * + *

The variant of UTF-8 implemented by this class is the restricted definition of UTF-8 + * introduced in Unicode 3.1. One implication of this is that it rejects "non-shortest form" byte sequences, + * even though the JDK decoder may accept them. + * + * @author Martin Buchholz + * @author Clément Roux + * @since 16.0 + */ +public final class Utf8 { + /** + * Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string, this + * method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in both + * time and space. + * + * @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired + * surrogates) + */ + public static int encodedLength(CharSequence sequence) { + // Warning to maintainers: this implementation is highly optimized. + int utf16Length = sequence.length(); + int utf8Length = utf16Length; + int i = 0; + + // This loop optimizes for pure ASCII. + while (i < utf16Length && sequence.charAt(i) < 0x80) { + i++; + } + + // This loop optimizes for chars less than 0x800. + for (; i < utf16Length; i++) { + char c = sequence.charAt(i); + if (c < 0x800) { + utf8Length += ((0x7f - c) >>> 31); // branch free! + } else { + utf8Length += encodedLengthGeneral(sequence, i); + break; + } + } + + if (utf8Length < utf16Length) { + // Necessary and sufficient condition for overflow because of maximum 3x expansion + throw new AerospikeException("UTF-8 length does not fit in int: " + (utf8Length + (1L << 32))); + } + return utf8Length; + } + + private static int encodedLengthGeneral(CharSequence sequence, int start) { + int utf16Length = sequence.length(); + int utf8Length = 0; + for (int i = start; i < utf16Length; i++) { + char c = sequence.charAt(i); + if (c < 0x800) { + utf8Length += (0x7f - c) >>> 31; // branch free! + } else { + utf8Length += 2; + // jdk7+: if (Character.isSurrogate(c)) { + if (MIN_SURROGATE <= c && c <= MAX_SURROGATE) { + // Check that we have a well-formed surrogate pair. + if (Character.codePointAt(sequence, i) == c) { + throw new AerospikeException("Unpaired surrogate at index " + i); + } + i++; + } + } + } + return utf8Length; + } +} diff --git a/src/main/java/com/aerospike/dsl/impl/DSLParserImpl.java b/src/main/java/com/aerospike/dsl/impl/DSLParserImpl.java index 9f15cf1..d7ef10a 100644 --- a/src/main/java/com/aerospike/dsl/impl/DSLParserImpl.java +++ b/src/main/java/com/aerospike/dsl/impl/DSLParserImpl.java @@ -1,6 +1,5 @@ package com.aerospike.dsl.impl; -import com.aerospike.client.cdt.CTX; import com.aerospike.dsl.ConditionLexer; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; @@ -11,6 +10,7 @@ import com.aerospike.dsl.PlaceholderValues; import com.aerospike.dsl.annotation.Beta; import com.aerospike.dsl.api.DSLParser; +import com.aerospike.dsl.client.cdt.CTX; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.visitor.ExpressionConditionVisitor; import org.antlr.v4.runtime.CharStreams; diff --git a/src/main/java/com/aerospike/dsl/parts/AbstractPart.java b/src/main/java/com/aerospike/dsl/parts/AbstractPart.java index d31fcd1..61c8659 100644 --- a/src/main/java/com/aerospike/dsl/parts/AbstractPart.java +++ b/src/main/java/com/aerospike/dsl/parts/AbstractPart.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.parts; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.query.Filter; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.query.Filter; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/CdtPart.java b/src/main/java/com/aerospike/dsl/parts/cdt/CdtPart.java index 0a96f4a..4c7c0f0 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/CdtPart.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/CdtPart.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.parts.cdt; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.parts.path.BasePath; import com.aerospike.dsl.parts.path.PathFunction; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndex.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndex.java index dbd6f56..c6bf3ad 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndex.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndex.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; public class ListIndex extends ListPart { diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndexRange.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndexRange.java index 1b4d24f..f6036cf 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndexRange.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListIndexRange.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListPart.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListPart.java index fa9911e..a71c5a3 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListPart.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListPart.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.ListReturnType; import com.aerospike.dsl.DslParseException; -import com.aerospike.dsl.parts.path.PathFunction; +import com.aerospike.dsl.client.cdt.ListReturnType; import com.aerospike.dsl.parts.cdt.CdtPart; +import com.aerospike.dsl.parts.path.PathFunction; import lombok.Getter; @Getter diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRank.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRank.java index 4e77d85..bde8baf 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRank.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRank.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; public class ListRank extends ListPart { diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRange.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRange.java index 5e3c14d..5f38372 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRange.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRange.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRangeRelative.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRangeRelative.java index 98ac1dc..9586357 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRangeRelative.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListRankRangeRelative.java @@ -1,15 +1,15 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; -import static com.aerospike.dsl.util.ParsingUtils.unquote; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class ListRankRangeRelative extends ListPart { private final boolean inverted; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListTypeDesignator.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListTypeDesignator.java index 3e4ec86..e82faa6 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListTypeDesignator.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListTypeDesignator.java @@ -1,10 +1,10 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; -import com.aerospike.dsl.parts.path.BasePath; import com.aerospike.dsl.parts.cdt.CdtPart; +import com.aerospike.dsl.parts.path.BasePath; import java.util.Collections; import java.util.List; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValue.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValue.java index 8cea8d2..a89d516 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValue.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValue.java @@ -1,10 +1,10 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; import static com.aerospike.dsl.util.ParsingUtils.unquote; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueList.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueList.java index a1a3cc4..537178e 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueList.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueList.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; import java.util.List; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueRange.java b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueRange.java index 51254f0..f7cab86 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueRange.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/list/ListValueRange.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.list; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.parts.path.BasePath; public class ListValueRange extends ListPart { diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndex.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndex.java index 25dd0a4..8561e47 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndex.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndex.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; public class MapIndex extends MapPart { diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRange.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRange.java index 4015869..e5fe7fe 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRange.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRange.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRangeRelative.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRangeRelative.java index b33628f..3962ef7 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRangeRelative.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapIndexRangeRelative.java @@ -1,15 +1,15 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; -import static com.aerospike.dsl.util.ParsingUtils.unquote; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class MapIndexRangeRelative extends MapPart { private final boolean inverted; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKey.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKey.java index 2f4b0ba..4c9de26 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKey.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKey.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; import static com.aerospike.dsl.util.ParsingUtils.unquote; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyList.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyList.java index 3b43e6b..a80498a 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyList.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyList.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; import java.util.List; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyRange.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyRange.java index 11dbc98..f5f33fd 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyRange.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapKeyRange.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; import java.util.Optional; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapPart.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapPart.java index 5550eba..f42a1eb 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapPart.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapPart.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.dsl.parts.path.PathFunction; +import com.aerospike.dsl.client.cdt.MapReturnType; import com.aerospike.dsl.parts.cdt.CdtPart; +import com.aerospike.dsl.parts.path.PathFunction; import lombok.Getter; @Getter diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRank.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRank.java index 9c84f13..4fc5823 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRank.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRank.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; public class MapRank extends MapPart { diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRange.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRange.java index ab6f81c..c8207c1 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRange.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRange.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRangeRelative.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRangeRelative.java index e703569..0e2491a 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRangeRelative.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapRankRangeRelative.java @@ -1,15 +1,15 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; -import static com.aerospike.dsl.util.ParsingUtils.unquote; import static com.aerospike.dsl.util.ParsingUtils.subtractNullable; +import static com.aerospike.dsl.util.ParsingUtils.unquote; public class MapRankRangeRelative extends MapPart { private final boolean inverted; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapTypeDesignator.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapTypeDesignator.java index b4983e9..9d4a352 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapTypeDesignator.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapTypeDesignator.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.dsl.parts.path.BasePath; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.cdt.CdtPart; +import com.aerospike.dsl.parts.path.BasePath; /** * Designates that the element to the left is a Map. diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValue.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValue.java index 5ff7e9f..afd1a56 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValue.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValue.java @@ -1,10 +1,10 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; import static com.aerospike.dsl.util.ParsingUtils.unquote; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueList.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueList.java index da3cdfd..a5c269b 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueList.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueList.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; import java.util.List; diff --git a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueRange.java b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueRange.java index 8cf5031..9230f5c 100644 --- a/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueRange.java +++ b/src/main/java/com/aerospike/dsl/parts/cdt/map/MapValueRange.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parts.cdt.map; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.path.BasePath; public class MapValueRange extends MapPart { diff --git a/src/main/java/com/aerospike/dsl/parts/operand/BooleanOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/BooleanOperand.java index be67ef0..431a3d2 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/BooleanOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/BooleanOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/operand/FloatOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/FloatOperand.java index e03bc9e..ca3ad04 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/FloatOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/FloatOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/operand/IntOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/IntOperand.java index fc21885..94f92d7 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/IntOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/IntOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/operand/ListOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/ListOperand.java index afc5b3e..244b029 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/ListOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/ListOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/operand/MapOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/MapOperand.java index 4a6efd9..ecc17c5 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/MapOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/MapOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/operand/MetadataOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/MetadataOperand.java index 3b3c18c..9059764 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/MetadataOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/MetadataOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.ExpressionContainer; import lombok.Getter; @@ -26,9 +26,7 @@ public MetadataOperand(String functionName, int parameter) { private Exp constructMetadataExp(String functionName, Integer parameter) { return switch (functionName) { - case "deviceSize" -> Exp.deviceSize(); - case "memorySize" -> Exp.memorySize(); - case "recordSize" -> Exp.recordSize(); + case "deviceSize", "recordSize", "memorySize" -> Exp.recordSize(); case "digestModulo" -> Exp.digestModulo(parameter); case "isTombstone" -> Exp.isTombstone(); case "keyExists" -> Exp.keyExists(); diff --git a/src/main/java/com/aerospike/dsl/parts/operand/StringOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/StringOperand.java index a33303b..c81ad49 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/StringOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/StringOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/aerospike/dsl/parts/operand/VariableOperand.java b/src/main/java/com/aerospike/dsl/parts/operand/VariableOperand.java index d15652c..d800760 100644 --- a/src/main/java/com/aerospike/dsl/parts/operand/VariableOperand.java +++ b/src/main/java/com/aerospike/dsl/parts/operand/VariableOperand.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.operand; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/path/BasePath.java b/src/main/java/com/aerospike/dsl/parts/path/BasePath.java index b6547e6..d6eecbd 100644 --- a/src/main/java/com/aerospike/dsl/parts/path/BasePath.java +++ b/src/main/java/com/aerospike/dsl/parts/path/BasePath.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.path; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/path/BinPart.java b/src/main/java/com/aerospike/dsl/parts/path/BinPart.java index aad9324..9ddf892 100644 --- a/src/main/java/com/aerospike/dsl/parts/path/BinPart.java +++ b/src/main/java/com/aerospike/dsl/parts/path/BinPart.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.path; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.ExpressionContainer; import lombok.Getter; import lombok.Setter; diff --git a/src/main/java/com/aerospike/dsl/parts/path/Path.java b/src/main/java/com/aerospike/dsl/parts/path/Path.java index 72301ff..744c2e3 100644 --- a/src/main/java/com/aerospike/dsl/parts/path/Path.java +++ b/src/main/java/com/aerospike/dsl/parts/path/Path.java @@ -1,7 +1,7 @@ package com.aerospike.dsl.parts.path; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.parts.cdt.CdtPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/parts/path/PathFunction.java b/src/main/java/com/aerospike/dsl/parts/path/PathFunction.java index 0e7291f..47e34d8 100644 --- a/src/main/java/com/aerospike/dsl/parts/path/PathFunction.java +++ b/src/main/java/com/aerospike/dsl/parts/path/PathFunction.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.parts.path; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import lombok.Getter; diff --git a/src/main/java/com/aerospike/dsl/util/PathOperandUtils.java b/src/main/java/com/aerospike/dsl/util/PathOperandUtils.java index b962ae5..f876970 100644 --- a/src/main/java/com/aerospike/dsl/util/PathOperandUtils.java +++ b/src/main/java/com/aerospike/dsl/util/PathOperandUtils.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.util; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; -import com.aerospike.client.exp.MapExp; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.parts.cdt.CdtPart; import com.aerospike.dsl.parts.cdt.list.ListPart; diff --git a/src/main/java/com/aerospike/dsl/util/TypeUtils.java b/src/main/java/com/aerospike/dsl/util/TypeUtils.java index dc33d60..85c66b1 100644 --- a/src/main/java/com/aerospike/dsl/util/TypeUtils.java +++ b/src/main/java/com/aerospike/dsl/util/TypeUtils.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.util; -import com.aerospike.client.exp.Exp; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.parts.cdt.map.MapTypeDesignator; import lombok.experimental.UtilityClass; diff --git a/src/main/java/com/aerospike/dsl/util/ValidationUtils.java b/src/main/java/com/aerospike/dsl/util/ValidationUtils.java index bfb261b..25bede7 100644 --- a/src/main/java/com/aerospike/dsl/util/ValidationUtils.java +++ b/src/main/java/com/aerospike/dsl/util/ValidationUtils.java @@ -1,7 +1,7 @@ package com.aerospike.dsl.util; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.exp.Exp; import lombok.experimental.UtilityClass; @UtilityClass diff --git a/src/main/java/com/aerospike/dsl/visitor/ExpressionConditionVisitor.java b/src/main/java/com/aerospike/dsl/visitor/ExpressionConditionVisitor.java index 9f77023..82c8253 100644 --- a/src/main/java/com/aerospike/dsl/visitor/ExpressionConditionVisitor.java +++ b/src/main/java/com/aerospike/dsl/visitor/ExpressionConditionVisitor.java @@ -1,9 +1,9 @@ package com.aerospike.dsl.visitor; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.ConditionBaseVisitor; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.parts.ExpressionContainer; import com.aerospike.dsl.parts.cdt.list.ListIndex; diff --git a/src/main/java/com/aerospike/dsl/visitor/NoApplicableFilterException.java b/src/main/java/com/aerospike/dsl/visitor/NoApplicableFilterException.java index 4b6813e..b602128 100644 --- a/src/main/java/com/aerospike/dsl/visitor/NoApplicableFilterException.java +++ b/src/main/java/com/aerospike/dsl/visitor/NoApplicableFilterException.java @@ -1,6 +1,6 @@ package com.aerospike.dsl.visitor; -import com.aerospike.client.query.Filter; +import com.aerospike.dsl.client.query.Filter; /** * Indicates that no applicable {@link Filter} could be generated for a given DSL expression. For internal use. diff --git a/src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java b/src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java index 397f069..9197aa6 100644 --- a/src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java +++ b/src/main/java/com/aerospike/dsl/visitor/VisitorUtils.java @@ -1,13 +1,13 @@ package com.aerospike.dsl.visitor; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.query.Filter; -import com.aerospike.client.query.IndexType; import com.aerospike.dsl.ConditionParser; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.Index; import com.aerospike.dsl.PlaceholderValues; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.query.Filter; +import com.aerospike.dsl.client.query.IndexType; import com.aerospike.dsl.parts.AbstractPart; import com.aerospike.dsl.parts.ExpressionContainer; import com.aerospike.dsl.parts.ExpressionContainer.ExprPartsOperation; diff --git a/src/test/java/com/aerospike/dsl/ctx/CtxTests.java b/src/test/java/com/aerospike/dsl/ctx/CtxTests.java index 529dd6c..5981568 100644 --- a/src/test/java/com/aerospike/dsl/ctx/CtxTests.java +++ b/src/test/java/com/aerospike/dsl/ctx/CtxTests.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.ctx; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; import com.aerospike.dsl.DslParseException; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; import org.junit.jupiter.api.Test; import static com.aerospike.dsl.util.TestUtils.parseCtx; diff --git a/src/test/java/com/aerospike/dsl/expression/ArithmeticExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/ArithmeticExpressionsTests.java index 4199abf..2fef7fe 100644 --- a/src/test/java/com/aerospike/dsl/expression/ArithmeticExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ArithmeticExpressionsTests.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/BinExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/BinExpressionsTests.java index 7e60521..4b3d235 100644 --- a/src/test/java/com/aerospike/dsl/expression/BinExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/BinExpressionsTests.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import org.junit.jupiter.api.Test; import static com.aerospike.dsl.util.TestUtils.parseFilterExp; diff --git a/src/test/java/com/aerospike/dsl/expression/CastingTests.java b/src/test/java/com/aerospike/dsl/expression/CastingTests.java index 61aba61..65cd4c3 100644 --- a/src/test/java/com/aerospike/dsl/expression/CastingTests.java +++ b/src/test/java/com/aerospike/dsl/expression/CastingTests.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/ControlStructuresTests.java b/src/test/java/com/aerospike/dsl/expression/ControlStructuresTests.java index 7e45860..72915d1 100644 --- a/src/test/java/com/aerospike/dsl/expression/ControlStructuresTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ControlStructuresTests.java @@ -1,7 +1,7 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/ExplicitTypesTests.java b/src/test/java/com/aerospike/dsl/expression/ExplicitTypesTests.java index d1dff38..331428d 100644 --- a/src/test/java/com/aerospike/dsl/expression/ExplicitTypesTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ExplicitTypesTests.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/ImplicitTypesTests.java b/src/test/java/com/aerospike/dsl/expression/ImplicitTypesTests.java index 714276e..998b658 100644 --- a/src/test/java/com/aerospike/dsl/expression/ImplicitTypesTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ImplicitTypesTests.java @@ -1,7 +1,7 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/ListExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/ListExpressionsTests.java index 0208226..5172782 100644 --- a/src/test/java/com/aerospike/dsl/expression/ListExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/ListExpressionsTests.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/LogicalExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/LogicalExpressionsTests.java index cb19931..f8c0426 100644 --- a/src/test/java/com/aerospike/dsl/expression/LogicalExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/LogicalExpressionsTests.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; import org.opentest4j.AssertionFailedError; diff --git a/src/test/java/com/aerospike/dsl/expression/MapAndListExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/MapAndListExpressionsTests.java index 16926e7..c508afa 100644 --- a/src/test/java/com/aerospike/dsl/expression/MapAndListExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/MapAndListExpressionsTests.java @@ -1,13 +1,13 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/MapExpressionsTests.java b/src/test/java/com/aerospike/dsl/expression/MapExpressionsTests.java index a9a527d..79718a8 100644 --- a/src/test/java/com/aerospike/dsl/expression/MapExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/expression/MapExpressionsTests.java @@ -1,13 +1,13 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.ListExp; -import com.aerospike.client.exp.MapExp; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.ListExp; +import com.aerospike.dsl.client.exp.MapExp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/expression/RecordMetadataTests.java b/src/test/java/com/aerospike/dsl/expression/RecordMetadataTests.java index 0c25243..62af9af 100644 --- a/src/test/java/com/aerospike/dsl/expression/RecordMetadataTests.java +++ b/src/test/java/com/aerospike/dsl/expression/RecordMetadataTests.java @@ -1,8 +1,8 @@ package com.aerospike.dsl.expression; -import com.aerospike.client.exp.Exp; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; +import com.aerospike.dsl.client.exp.Exp; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; @@ -15,7 +15,7 @@ class RecordMetadataTests { void deviceSize() { // Expression to find records that occupy more than 1 MiB of storage space ExpressionContext input = ExpressionContext.of("$.deviceSize() > 1048576"); - Exp expected = Exp.gt(Exp.deviceSize(), Exp.val(1024 * 1024)); + Exp expected = Exp.gt(Exp.recordSize(), Exp.val(1024 * 1024)); TestUtils.parseFilterExpressionAndCompare(input, expected); } @@ -23,7 +23,7 @@ void deviceSize() { void memorySize() { // Expression to find records that occupy more than 1 MiB of memory ExpressionContext input = ExpressionContext.of("$.memorySize() > 1048576"); - Exp expected = Exp.gt(Exp.memorySize(), Exp.val(1024 * 1024)); + Exp expected = Exp.gt(Exp.recordSize(), Exp.val(1024 * 1024)); TestUtils.parseFilterExpressionAndCompare(input, expected); } @@ -146,7 +146,7 @@ void metadataWithLogicalOperatorsExpressions() { // test AND ExpressionContext input = ExpressionContext.of("$.deviceSize() > 1024 and $.ttl() < 300"); Exp expected = Exp.and( - Exp.gt(Exp.deviceSize(), Exp.val(1024)), + Exp.gt(Exp.recordSize(), Exp.val(1024)), Exp.lt(Exp.ttl(), Exp.val(300)) ); TestUtils.parseFilterExpressionAndCompare(input, expected); @@ -154,7 +154,7 @@ void metadataWithLogicalOperatorsExpressions() { // test OR ExpressionContext input2 = ExpressionContext.of("$.deviceSize() > 1024 or $.ttl() < 300"); Exp expected2 = Exp.or( - Exp.gt(Exp.deviceSize(), Exp.val(1024)), + Exp.gt(Exp.recordSize(), Exp.val(1024)), Exp.lt(Exp.ttl(), Exp.val(300)) ); TestUtils.parseFilterExpressionAndCompare(input2, expected2); diff --git a/src/test/java/com/aerospike/dsl/filter/ArithmeticFiltersTests.java b/src/test/java/com/aerospike/dsl/filter/ArithmeticFiltersTests.java index 128b7be..929328c 100644 --- a/src/test/java/com/aerospike/dsl/filter/ArithmeticFiltersTests.java +++ b/src/test/java/com/aerospike/dsl/filter/ArithmeticFiltersTests.java @@ -1,10 +1,10 @@ package com.aerospike.dsl.filter; -import com.aerospike.client.query.Filter; -import com.aerospike.client.query.IndexType; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.Index; import com.aerospike.dsl.IndexContext; +import com.aerospike.dsl.client.query.Filter; +import com.aerospike.dsl.client.query.IndexType; import org.junit.jupiter.api.Test; import java.util.Collection; diff --git a/src/test/java/com/aerospike/dsl/filter/BinFiltersTests.java b/src/test/java/com/aerospike/dsl/filter/BinFiltersTests.java index 98745de..2f53aba 100644 --- a/src/test/java/com/aerospike/dsl/filter/BinFiltersTests.java +++ b/src/test/java/com/aerospike/dsl/filter/BinFiltersTests.java @@ -1,10 +1,10 @@ package com.aerospike.dsl.filter; -import com.aerospike.client.query.Filter; -import com.aerospike.client.query.IndexType; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.Index; import com.aerospike.dsl.IndexContext; +import com.aerospike.dsl.client.query.Filter; +import com.aerospike.dsl.client.query.IndexType; import org.junit.jupiter.api.Test; import java.util.List; diff --git a/src/test/java/com/aerospike/dsl/filter/ExplicitTypesFiltersTests.java b/src/test/java/com/aerospike/dsl/filter/ExplicitTypesFiltersTests.java index 224cce7..1174bc4 100644 --- a/src/test/java/com/aerospike/dsl/filter/ExplicitTypesFiltersTests.java +++ b/src/test/java/com/aerospike/dsl/filter/ExplicitTypesFiltersTests.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.filter; -import com.aerospike.client.query.Filter; -import com.aerospike.client.query.IndexType; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.Index; import com.aerospike.dsl.IndexContext; +import com.aerospike.dsl.client.query.Filter; +import com.aerospike.dsl.client.query.IndexType; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/filter/ListExpressionsTests.java b/src/test/java/com/aerospike/dsl/filter/ListExpressionsTests.java index f03ca4f..24d50de 100644 --- a/src/test/java/com/aerospike/dsl/filter/ListExpressionsTests.java +++ b/src/test/java/com/aerospike/dsl/filter/ListExpressionsTests.java @@ -1,18 +1,18 @@ package com.aerospike.dsl.filter; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.query.Filter; -import com.aerospike.client.query.IndexType; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.Index; import com.aerospike.dsl.IndexContext; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.query.Filter; +import com.aerospike.dsl.client.query.IndexType; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; import java.util.List; -import static com.aerospike.client.query.IndexCollectionType.LIST; +import static com.aerospike.dsl.client.query.IndexCollectionType.LIST; class ListExpressionsTests { diff --git a/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java b/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java index 3982d8d..bdd6585 100644 --- a/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java +++ b/src/test/java/com/aerospike/dsl/parsedExpression/LogicalParsedExpressionTests.java @@ -1,11 +1,11 @@ package com.aerospike.dsl.parsedExpression; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.query.Filter; -import com.aerospike.client.query.IndexType; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.Index; import com.aerospike.dsl.IndexContext; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.query.Filter; +import com.aerospike.dsl.client.query.IndexType; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/parsedExpression/PlaceholdersTests.java b/src/test/java/com/aerospike/dsl/parsedExpression/PlaceholdersTests.java index 9a555c4..241a90d 100644 --- a/src/test/java/com/aerospike/dsl/parsedExpression/PlaceholdersTests.java +++ b/src/test/java/com/aerospike/dsl/parsedExpression/PlaceholdersTests.java @@ -1,15 +1,5 @@ package com.aerospike.dsl.parsedExpression; -import com.aerospike.client.Value; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.cdt.ListReturnType; -import com.aerospike.client.cdt.MapReturnType; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.Expression; -import com.aerospike.client.exp.ListExp; -import com.aerospike.client.exp.MapExp; -import com.aerospike.client.query.Filter; -import com.aerospike.client.query.IndexType; import com.aerospike.dsl.DslParseException; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.Index; @@ -17,6 +7,16 @@ import com.aerospike.dsl.ParseResult; import com.aerospike.dsl.ParsedExpression; import com.aerospike.dsl.PlaceholderValues; +import com.aerospike.dsl.client.Value; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.cdt.ListReturnType; +import com.aerospike.dsl.client.cdt.MapReturnType; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.Expression; +import com.aerospike.dsl.client.exp.ListExp; +import com.aerospike.dsl.client.exp.MapExp; +import com.aerospike.dsl.client.query.Filter; +import com.aerospike.dsl.client.query.IndexType; import com.aerospike.dsl.util.TestUtils; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/aerospike/dsl/util/TestUtils.java b/src/test/java/com/aerospike/dsl/util/TestUtils.java index 351a197..ca8959f 100644 --- a/src/test/java/com/aerospike/dsl/util/TestUtils.java +++ b/src/test/java/com/aerospike/dsl/util/TestUtils.java @@ -1,12 +1,12 @@ package com.aerospike.dsl.util; -import com.aerospike.client.cdt.CTX; -import com.aerospike.client.exp.Exp; -import com.aerospike.client.exp.Expression; -import com.aerospike.client.query.Filter; import com.aerospike.dsl.ExpressionContext; import com.aerospike.dsl.IndexContext; import com.aerospike.dsl.ParsedExpression; +import com.aerospike.dsl.client.cdt.CTX; +import com.aerospike.dsl.client.exp.Exp; +import com.aerospike.dsl.client.exp.Expression; +import com.aerospike.dsl.client.query.Filter; import com.aerospike.dsl.impl.DSLParserImpl; import lombok.experimental.UtilityClass; From 1d6cb6c96c3860c9935f7e8ac2600c225ec0b018 Mon Sep 17 00:00:00 2001 From: agrgr Date: Mon, 1 Dec 2025 22:55:15 +0100 Subject: [PATCH 2/6] update pom.xml --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index bb2c0c9..16782ff 100644 --- a/pom.xml +++ b/pom.xml @@ -63,11 +63,6 @@ - - - - - org.antlr antlr4-runtime From d3d679b7b0006d0bac504b7dc3af1110008619ba Mon Sep 17 00:00:00 2001 From: agrgr Date: Mon, 1 Dec 2025 22:57:59 +0100 Subject: [PATCH 3/6] update codeql version --- .github/workflows/snyk-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/snyk-scan.yml b/.github/workflows/snyk-scan.yml index 5287bf7..52fe198 100644 --- a/.github/workflows/snyk-scan.yml +++ b/.github/workflows/snyk-scan.yml @@ -37,6 +37,6 @@ jobs: - name: Upload result to GitHub Code Scanning if: steps.out-file.outputs.exists == 'true' - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@v4 with: sarif_file: snyk.sarif \ No newline at end of file From edecc126bd89d6dfb03770fbd88561035a28a720 Mon Sep 17 00:00:00 2001 From: agrgr Date: Mon, 1 Dec 2025 23:12:13 +0100 Subject: [PATCH 4/6] remove unused file --- .../aerospike/dsl/client/command/Buffer.java | 52 --------- .../aerospike/dsl/client/command/Command.java | 109 ------------------ .../aerospike/dsl/client/exp/Expression.java | 9 -- 3 files changed, 170 deletions(-) delete mode 100644 src/main/java/com/aerospike/dsl/client/command/Command.java diff --git a/src/main/java/com/aerospike/dsl/client/command/Buffer.java b/src/main/java/com/aerospike/dsl/client/command/Buffer.java index 8ea063e..5fcbf5f 100644 --- a/src/main/java/com/aerospike/dsl/client/command/Buffer.java +++ b/src/main/java/com/aerospike/dsl/client/command/Buffer.java @@ -95,58 +95,6 @@ public static Object bytesToParticle(int type, byte[] buf, int offset, int len) } } - /* - private static Object parseList(byte[] buf, int offset, int len) throws AerospikeException { - int limit = offset + len; - int itemCount = Buffer.bytesToInt(buf, offset); - offset += 4; - ArrayList list = new ArrayList(itemCount); - - while (offset < limit) { - int sz = Buffer.bytesToInt(buf, offset); - offset += 4; - int type = buf[offset]; - offset++; - list.add(bytesToParticle(type, buf, offset, sz)); - offset += sz; - } - return list; - } - - private static Object parseMap(byte[] buf, int offset, int len) throws AerospikeException { - Object key; - Object value; - - int limit = offset + len; - int n_items = Buffer.bytesToInt(buf, offset); - offset += 4; - HashMap map = new HashMap(n_items); - - while (offset < limit) { - // read out the key - int sz = Buffer.bytesToInt(buf, offset); - offset += 4; - int type = buf[offset]; - offset++; - - key = bytesToParticle(type, buf, offset, len); - offset += sz; - - // read out the value - sz = Buffer.bytesToInt(buf, offset); - offset += 4; - type = buf[offset]; - offset++; - - value = bytesToParticle(type, buf, offset, len); - offset += sz; - - map.put(key, value); - } - return map; - } - */ - /** * Estimate size of Utf8 encoded bytes without performing the actual encoding. */ diff --git a/src/main/java/com/aerospike/dsl/client/command/Command.java b/src/main/java/com/aerospike/dsl/client/command/Command.java deleted file mode 100644 index 6882533..0000000 --- a/src/main/java/com/aerospike/dsl/client/command/Command.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012-2025 Aerospike, Inc. - * - * Portions may be licensed to Aerospike, Inc. under one or more contributor - * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0. - * - * Licensed 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 com.aerospike.dsl.client.command; - -public class Command { - public static final int INFO1_READ = (1 << 0); // Contains a read operation. - public static final int INFO1_GET_ALL = (1 << 1); // Get all bins. - public static final int INFO1_SHORT_QUERY = (1 << 2); // Short query. - public static final int INFO1_BATCH = (1 << 3); // Batch read or exists. - public static final int INFO1_XDR = (1 << 4); // Operation is being performed by XDR. - public static final int INFO1_NOBINDATA = (1 << 5); // Do not read the bins. - public static final int INFO1_READ_MODE_AP_ALL = (1 << 6); // Involve all replicas in read operation. - public static final int INFO1_COMPRESS_RESPONSE = (1 << 7); // Tell server to compress it's response. - - public static final int INFO2_WRITE = (1 << 0); // Create or update record - public static final int INFO2_DELETE = (1 << 1); // Fling a record into the belly of Moloch. - public static final int INFO2_GENERATION = (1 << 2); // Update if expected generation == old. - public static final int INFO2_GENERATION_GT = (1 << 3); // Update if new generation >= old, good for restore. - public static final int INFO2_DURABLE_DELETE = (1 << 4); // Command resulting in record deletion leaves tombstone (Enterprise only). - public static final int INFO2_CREATE_ONLY = (1 << 5); // Create only. Fail if record already exists. - public static final int INFO2_RELAX_AP_LONG_QUERY = (1 << 6); // Treat as long query, but relax read consistency. - public static final int INFO2_RESPOND_ALL_OPS = (1 << 7); // Return a result for every operation. - - public static final int INFO3_LAST = (1 << 0); // This is the last of a multi-part message. - public static final int INFO3_COMMIT_MASTER = (1 << 1); // Commit to master only before declaring success. - // On send: Do not return partition done in scan/query. - // On receive: Specified partition is done in scan/query. - public static final int INFO3_PARTITION_DONE = (1 << 2); - public static final int INFO3_UPDATE_ONLY = (1 << 3); // Update only. Merge bins. - public static final int INFO3_CREATE_OR_REPLACE = (1 << 4); // Create or completely replace record. - public static final int INFO3_REPLACE_ONLY = (1 << 5); // Completely replace existing record only. - public static final int INFO3_SC_READ_TYPE = (1 << 6); // See below. - public static final int INFO3_SC_READ_RELAX = (1 << 7); // See below. - - // Interpret SC_READ bits in info3. - // - // RELAX TYPE - // strict - // ------ - // 0 0 sequential (default) - // 0 1 linearize - // - // relaxed - // ------- - // 1 0 allow replica - // 1 1 allow unavailable - - public static final int INFO4_TXN_VERIFY_READ = (1 << 0); // Send transaction version to the server to be verified. - public static final int INFO4_TXN_ROLL_FORWARD = (1 << 1); // Roll forward transaction. - public static final int INFO4_TXN_ROLL_BACK = (1 << 2); // Roll back transaction. - public static final int INFO4_TXN_ON_LOCKING_ONLY = (1 << 4); // Must be able to lock record in transaction. - - public static final byte STATE_READ_AUTH_HEADER = 1; - public static final byte STATE_READ_HEADER = 2; - public static final byte STATE_READ_DETAIL = 3; - public static final byte STATE_COMPLETE = 4; - - public static final byte BATCH_MSG_READ = 0x0; - public static final byte BATCH_MSG_REPEAT = 0x1; - public static final byte BATCH_MSG_INFO = 0x2; - public static final byte BATCH_MSG_GEN = 0x4; - public static final byte BATCH_MSG_TTL = 0x8; - public static final byte BATCH_MSG_INFO4 = 0x10; - - public static final int MSG_TOTAL_HEADER_SIZE = 30; - public static final int FIELD_HEADER_SIZE = 5; - public static final int OPERATION_HEADER_SIZE = 8; - public static final int MSG_REMAINING_HEADER_SIZE = 22; - public static final int COMPRESS_THRESHOLD = 128; - public static final long CL_MSG_VERSION = 2L; - public static final long AS_MSG_TYPE = 3L; - public static final long MSG_TYPE_COMPRESSED = 4L; - - public byte[] dataBuffer; - public int dataOffset; - public final int maxRetries; - public final int serverTimeout; - public int socketTimeout; - public int totalTimeout; - public Long version; - - public Command(int socketTimeout, int totalTimeout, int maxRetries) { - this.maxRetries = maxRetries; - this.totalTimeout = totalTimeout; - - if (totalTimeout > 0) { - this.socketTimeout = (socketTimeout < totalTimeout && socketTimeout > 0)? socketTimeout : totalTimeout; - this.serverTimeout = this.socketTimeout; - } - else { - this.socketTimeout = socketTimeout; - this.serverTimeout = 0; - } - } -} diff --git a/src/main/java/com/aerospike/dsl/client/exp/Expression.java b/src/main/java/com/aerospike/dsl/client/exp/Expression.java index 51edf2f..10171ce 100644 --- a/src/main/java/com/aerospike/dsl/client/exp/Expression.java +++ b/src/main/java/com/aerospike/dsl/client/exp/Expression.java @@ -16,7 +16,6 @@ */ package com.aerospike.dsl.client.exp; -import com.aerospike.dsl.client.command.Command; import com.aerospike.dsl.client.util.Crypto; import com.aerospike.dsl.client.util.Packer; @@ -88,14 +87,6 @@ public String getBase64() { return Crypto.encodeBase64(bytes); } - /** - * Estimate expression size in wire protocol. - * For internal use only. - */ - public int size() { - return bytes.length + Command.FIELD_HEADER_SIZE; - } - @Override public int hashCode() { final int prime = 31; From 9acb0921297ec7e7b5f3477c1cc1d4d3e9627c86 Mon Sep 17 00:00:00 2001 From: agrgr Date: Thu, 4 Dec 2025 19:15:01 +0100 Subject: [PATCH 5/6] replace logback-classic with slf4j-simple and slf4j-api --- pom.xml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 16782ff..c4c6847 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 3.27.6 0.8.0 2.0.1 - 1.5.21 + 2.0.17 @@ -79,9 +79,15 @@ ${gnu.crypto.version} - ch.qos.logback - logback-classic - ${logback.version} + org.slf4j + slf4j-simple + ${slf4j-simple.version} + test + + + org.slf4j + slf4j-api + ${slf4j-simple.version} org.junit.jupiter From 46365f005fefbd3b2af2cb6232c4eb9bcd83971f Mon Sep 17 00:00:00 2001 From: agrgr Date: Thu, 4 Dec 2025 19:41:50 +0100 Subject: [PATCH 6/6] add toString() implementations to Exp --- .../com/aerospike/dsl/client/exp/Exp.java | 294 +++++++++++++++++- 1 file changed, 284 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/aerospike/dsl/client/exp/Exp.java b/src/main/java/com/aerospike/dsl/client/exp/Exp.java index 4bf6a5d..9a186b1 100644 --- a/src/main/java/com/aerospike/dsl/client/exp/Exp.java +++ b/src/main/java/com/aerospike/dsl/client/exp/Exp.java @@ -20,6 +20,7 @@ import com.aerospike.dsl.client.query.RegexFlag; import com.aerospike.dsl.client.util.Packer; +import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.Map; @@ -402,14 +403,25 @@ public static Exp digestModulo(int mod) { * Exp.regexCompare("prefix.*suffix", RegexFlag.ICASE | RegexFlag.NEWLINE, Exp.stringBin("a")) * } * - * @param regex regular expression string - * @param flags regular expression bit flags. See {@link RegexFlag} - * @param bin string bin or string value expression + * @param regex regular expression string + * @param flags regular expression bit flags. See {@link RegexFlag} + * @param bin string bin or string value expression */ public static Exp regexCompare(String regex, int flags, Exp bin) { return new Regex(bin, regex, flags); } + protected String expsAsString(Exp[] exps) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < exps.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(exps[i].toString()); + } + return sb.toString(); + } + //-------------------------------------------------- // GEO Spatial //-------------------------------------------------- @@ -499,7 +511,7 @@ public static Exp val(List list) { * Create map value. For ordered maps, pass in a TreeMap or a map that implements the SortedMap * interface. For unordered maps, pass in a HashMap. */ - public static Exp val(Map map) { + public static Exp val(Map map) { return new MapVal(map); } @@ -1237,6 +1249,69 @@ public static Exp expr(Expression e) { public abstract void pack(Packer packer); + // Package visible + static String cmdAsString(int cmd) { + return switch (cmd) { + case UNKNOWN -> "UNKNOWN"; + case EQ -> "EQ"; + case NE -> "NE"; + case GT -> "GT"; + case GE -> "GE"; + case LT -> "LT"; + case LE -> "LE"; + case REGEX -> "REGEX"; + case GEO -> "GEO"; + case AND -> "AND"; + case OR -> "OR"; + case NOT -> "NOT"; + case EXCLUSIVE -> "EXCLUSIVE"; + case ADD -> "ADD"; + case SUB -> "SUB"; + case MUL -> "MUL"; + case DIV -> "DIV"; + case POW -> "POW"; + case LOG -> "LOG"; + case MOD -> "MOD"; + case ABS -> "ABS"; + case FLOOR -> "FLOOR"; + case CEIL -> "CEIL"; + case TO_INT -> "TO_INT"; + case TO_FLOAT -> "TO_FLOAT"; + case INT_AND -> "INT_AND"; + case INT_OR -> "INT_OR"; + case INT_XOR -> "INT_XOR"; + case INT_NOT -> "INT_NOT"; + case INT_LSHIFT -> "INT_LSHIFT"; + case INT_RSHIFT -> "INT_RSHIFT"; + case INT_ARSHIFT -> "INT_ARSHIFT"; + case INT_COUNT -> "INT_COUNT"; + case INT_LSCAN -> "INT_LSCAN"; + case INT_RSCAN -> "INT_RSCAN"; + case MIN -> "MIN"; + case MAX -> "MAX"; + case DIGEST_MODULO -> "DIGEST_MODULO"; + case DEVICE_SIZE -> "DEVICE_SIZE"; + case LAST_UPDATE -> "LAST_UPDATE"; + case SINCE_UPDATE -> "SINCE_UPDATE"; + case VOID_TIME -> "VOID_TIME"; + case TTL -> "TTL"; + case SET_NAME -> "SET_NAME"; + case KEY_EXISTS -> "KEY_EXISTS"; + case IS_TOMBSTONE -> "IS_TOMBSTONE"; + case MEMORY_SIZE -> "MEMORY_SIZE"; + case RECORD_SIZE -> "RECORD_SIZE"; + case KEY -> "KEY"; + case BIN -> "BIN"; + case BIN_TYPE -> "BIN_TYPE"; + case COND -> "COND"; + case VAR -> "VAR"; + case LET -> "LET"; + case QUOTED -> "QUOTED"; + case CALL -> "CALL"; + default -> null; + }; + } + /** * For internal use only. */ @@ -1253,6 +1328,11 @@ public Module(Exp bin, byte[] bytes, int retType, int module) { this.module = module; } + public String toString() { + return "Module [bin=" + bin + ", bytes=" + Arrays.toString(bytes) + ", retType=" + retType + + ", module=" + module + "]"; + } + @Override public void pack(Packer packer) { packer.packArrayBegin(5); @@ -1273,6 +1353,10 @@ public Bin(String name, Type type) { this.type = type; } + public String toString() { + return "Bin(" + type.toString().charAt(0) + "\"" + name + "\")"; + } + @Override public void pack(Packer packer) { packer.packArrayBegin(3); @@ -1293,6 +1377,10 @@ private Regex(Exp bin, String regex, int flags) { this.flags = flags; } + public String toString() { + return "Regex [bin=" + bin + ", regex=" + regex + ", flags=" + flags + "]"; + } + @Override public void pack(Packer packer) { packer.packArrayBegin(4); @@ -1310,6 +1398,11 @@ private Let(Exp... exps) { this.exps = exps; } + @Override + public String toString() { + return "Let [exps=" + expsAsString(exps) + "]"; + } + @Override public void pack(Packer packer) { // Let wire format: LET , , , , ..., @@ -1332,6 +1425,11 @@ private Def(String name, Exp exp) { this.exp = exp; } + @Override + public String toString() { + return "Def [name=" + name + ", exp=" + exp + "]"; + } + @Override public void pack(Packer packer) { packer.packString(name); @@ -1348,6 +1446,95 @@ private CmdExp(int cmd, Exp... exps) { this.cmd = cmd; } + @Override + public String toString() { + String cmdStr = cmdAsString(cmd); + if (cmdStr == null) { + return "CmdExp [exps=" + expsAsString(exps) + ", cmd=" + cmd + "]"; + } + switch (cmd) { + case GT: + return multiArgOp(">"); + case GE: + return multiArgOp(">="); + case LE: + return multiArgOp("<="); + case LT: + return multiArgOp("<"); + case EQ: + return multiArgOp("=="); + case NE: + return multiArgOp("!="); + case ADD: + return multiArgOp("+"); + case SUB: + return multiArgOp("-"); + case MUL: + return multiArgOp("*"); + case DIV: + return multiArgOp("/"); + case AND: + return multiArgOp(" AND "); + case OR: + return multiArgOp(" OR "); + case INT_AND: + return multiArgOp("&"); + case INT_ARSHIFT: + return multiArgOp(">>"); + case INT_LSHIFT: + return multiArgOp("<<"); + case INT_OR: + return multiArgOp("|"); + case INT_RSHIFT: + return multiArgOp(">>>"); + case COND: + // Special case + return formatCond(); + default: + return cmdStr + "(" + expsAsString(exps) + ")"; + + } + } + + private String multiArgOp(String separator) { + StringBuilder sb = new StringBuilder().append('('); + for (int i = 0; i < exps.length; i++) { + if (i > 0) { + sb.append(' ').append(separator).append(' '); + } + sb.append(exps[i].toString()); + } + return sb.append(')').toString(); + } + + private String formatCond() { + StringBuilder sb = new StringBuilder(); + if (exps.length == 0) { + return "COND()"; + } else if (exps.length == 1) { + return "COND(" + exps[0].toString() + ")"; + } + for (int i = 0; i < exps.length; i += 2) { + boolean needsThen = true; + int index = i + 1; + if (i == 0) { + sb.append("IF ("); + } else if (i < (exps.length - 1)) { + sb.append(" ELSE IF ("); + } else { + needsThen = false; + } + if (needsThen) { + sb.append(exps[i].toString()).append(") THEN "); + } else { + sb.append(" ELSE "); + index = i; + } + sb.append(exps[index].toString()); + } + return sb.toString(); + } + @Override public void pack(Packer packer) { packer.packArrayBegin(exps.length + 1); @@ -1368,6 +1555,15 @@ private CmdInt(int cmd, int val) { this.val = val; } + @Override + public String toString() { + String cmdStr = cmdAsString(cmd); + if (cmdStr == null) { + return "CmdInt [cmd=" + cmd + ", val=" + val + "]"; + } + return cmdStr + "(" + val + ")"; + } + @Override public void pack(Packer packer) { packer.packArrayBegin(2); @@ -1385,6 +1581,15 @@ private CmdStr(int cmd, String str) { this.cmd = cmd; } + @Override + public String toString() { + String cmdStr = cmdAsString(cmd); + if (cmdStr == null) { + return "CmdStr [str=" + str + ", cmd=" + cmd + "]"; + } + return cmdStr + "(\"" + str + "\")"; + } + @Override public void pack(Packer packer) { packer.packArrayBegin(2); @@ -1400,6 +1605,17 @@ private Cmd(int cmd) { this.cmd = cmd; } + @Override + public String toString() { + String cmdStr = cmdAsString(cmd); + if (cmdStr == null) { + return "Cmd [cmd=" + cmd + "]"; + + } else { + return cmdStr; + } + } + @Override public void pack(Packer packer) { packer.packArrayBegin(1); @@ -1414,6 +1630,11 @@ private Bool(boolean val) { this.val = val; } + @Override + public String toString() { + return "Bool [val=" + val + "]"; + } + @Override public void pack(Packer packer) { packer.packBoolean(val); @@ -1427,6 +1648,11 @@ private Int(long val) { this.val = val; } + @Override + public String toString() { + return val + "L"; + } + @Override public void pack(Packer packer) { packer.packLong(val); @@ -1440,6 +1666,11 @@ private Float(double val) { this.val = val; } + @Override + public String toString() { + return val + "f"; + } + @Override public void pack(Packer packer) { packer.packDouble(val); @@ -1454,6 +1685,11 @@ private Str(String val) { this.val = val; } + @Override + public String toString() { + return "\"" + val + "\""; + } + @Override public void pack(Packer packer) { packer.packParticleString(val); @@ -1467,6 +1703,11 @@ private Geo(String val) { this.val = val; } + @Override + public String toString() { + return "Geo [val=" + val + "]"; + } + @Override public void pack(Packer packer) { packer.packGeoJSON(val); @@ -1480,6 +1721,11 @@ private Blob(byte[] val) { this.val = val; } + @Override + public String toString() { + return "Blob [val=" + Arrays.toString(val) + "]"; + } + @Override public void pack(Packer packer) { packer.packParticleBytes(val); @@ -1494,6 +1740,11 @@ private ListVal(List list) { this.list = list; } + @Override + public String toString() { + return "ListVal [list=" + list + "]"; + } + @Override public void pack(Packer packer) { // List values need an extra array and QUOTED in order to distinguish @@ -1505,12 +1756,17 @@ public void pack(Packer packer) { } private static final class MapVal extends Exp { - private final Map map; + private final Map map; - private MapVal(Map map) { + private MapVal(Map map) { this.map = map; } + @Override + public String toString() { + return "MapVal [map=" + map + "]"; + } + @Override public void pack(Packer packer) { packer.packMap(map); @@ -1518,6 +1774,11 @@ public void pack(Packer packer) { } private static final class Nil extends Exp { + @Override + public String toString() { + return "Nil"; + } + @Override public void pack(Packer packer) { packer.packNil(); @@ -1525,6 +1786,11 @@ public void pack(Packer packer) { } private static final class Infinity extends Exp { + @Override + public String toString() { + return "Infinity []"; + } + @Override public void pack(Packer packer) { packer.packInfinity(); @@ -1532,21 +1798,29 @@ public void pack(Packer packer) { } private static final class Wildcard extends Exp { + @Override + public String toString() { + return "Wildcard []"; + } + @Override public void pack(Packer packer) { packer.packWildcard(); } } - private static final class ExpBytes extends Exp - { + private static final class ExpBytes extends Exp { private final byte[] bytes; - private ExpBytes(Expression e) - { + private ExpBytes(Expression e) { this.bytes = e.getBytes(); } + @Override + public String toString() { + return "ExpBytes [bytes=" + Arrays.toString(bytes) + "]"; + } + @Override public void pack(Packer packer) { packer.packByteArray(bytes, 0, bytes.length);