UNPKG

@gridgain/thin-client

Version:

NodeJS Client for Gridgain Community Edition

726 lines (681 loc) 24.6 kB
/* * Copyright 2019 GridGain Systems, Inc. and Contributors. * * Licensed under the GridGain Community Edition License (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.gridgain.com/products/software/community-edition/gridgain-community-edition-license * * 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. */ 'use strict'; const Decimal = require('decimal.js'); const Long = require('long'); const ObjectType = require('../ObjectType').ObjectType; const CompositeType = require('../ObjectType').CompositeType; const MapObjectType = require('../ObjectType').MapObjectType; const CollectionObjectType = require('../ObjectType').CollectionObjectType; const ComplexObjectType = require('../ObjectType').ComplexObjectType; const ObjectArrayType = require('../ObjectType').ObjectArrayType; const Timestamp = require('../Timestamp'); const EnumItem = require('../EnumItem'); const Errors = require('../Errors'); const ArgumentChecker = require('./ArgumentChecker'); // Operation codes const OPERATION = Object.freeze({ // Key-Value Queries CACHE_GET : 1000, CACHE_PUT : 1001, CACHE_PUT_IF_ABSENT : 1002, CACHE_GET_ALL : 1003, CACHE_PUT_ALL : 1004, CACHE_GET_AND_PUT : 1005, CACHE_GET_AND_REPLACE : 1006, CACHE_GET_AND_REMOVE : 1007, CACHE_GET_AND_PUT_IF_ABSENT : 1008, CACHE_REPLACE : 1009, CACHE_REPLACE_IF_EQUALS : 1010, CACHE_CONTAINS_KEY : 1011, CACHE_CONTAINS_KEYS : 1012, CACHE_CLEAR : 1013, CACHE_CLEAR_KEY : 1014, CACHE_CLEAR_KEYS : 1015, CACHE_REMOVE_KEY : 1016, CACHE_REMOVE_IF_EQUALS : 1017, CACHE_REMOVE_KEYS : 1018, CACHE_REMOVE_ALL : 1019, CACHE_GET_SIZE : 1020, CACHE_LOCAL_PEEK : 1021, // Cache Configuration CACHE_GET_NAMES : 1050, CACHE_CREATE_WITH_NAME : 1051, CACHE_GET_OR_CREATE_WITH_NAME : 1052, CACHE_CREATE_WITH_CONFIGURATION : 1053, CACHE_GET_OR_CREATE_WITH_CONFIGURATION : 1054, CACHE_GET_CONFIGURATION : 1055, CACHE_DESTROY : 1056, CACHE_PARTITIONS : 1101, // SQL and Scan Queries QUERY_SCAN : 2000, QUERY_SCAN_CURSOR_GET_PAGE : 2001, QUERY_SQL : 2002, QUERY_SQL_CURSOR_GET_PAGE : 2003, QUERY_SQL_FIELDS : 2004, QUERY_SQL_FIELDS_CURSOR_GET_PAGE : 2005, RESOURCE_CLOSE : 0, // Binary Types GET_BINARY_TYPE : 3002, PUT_BINARY_TYPE : 3003 }); const TYPE_CODE = Object.assign({ BINARY_OBJECT : 27, BINARY_ENUM : 38 }, ObjectType.PRIMITIVE_TYPE, ObjectType.COMPOSITE_TYPE); const TYPE_INFO = Object.freeze({ [TYPE_CODE.BYTE] : { NAME : 'byte', SIZE : 1 }, [TYPE_CODE.SHORT] : { NAME : 'short', SIZE : 2 }, [TYPE_CODE.INTEGER] : { NAME : 'integer', SIZE : 4 }, [TYPE_CODE.LONG] : { NAME : 'long', SIZE : 8 }, [TYPE_CODE.FLOAT] : { NAME : 'float', SIZE : 4 }, [TYPE_CODE.DOUBLE] : { NAME : 'double', SIZE : 8 }, [TYPE_CODE.CHAR] : { NAME : 'char', SIZE : 2 }, [TYPE_CODE.BOOLEAN] : { NAME : 'boolean', SIZE : 1 }, [TYPE_CODE.STRING] : { NAME : 'string', NULLABLE : true }, [TYPE_CODE.UUID] : { NAME : 'UUID', SIZE : 16, NULLABLE : true }, [TYPE_CODE.DATE] : { NAME : 'date', SIZE : 8, NULLABLE : true }, [TYPE_CODE.BYTE_ARRAY] : { NAME : 'byte array', ELEMENT_TYPE : TYPE_CODE.BYTE, NULLABLE : true }, [TYPE_CODE.SHORT_ARRAY] : { NAME : 'short array', ELEMENT_TYPE : TYPE_CODE.SHORT, NULLABLE : true }, [TYPE_CODE.INTEGER_ARRAY] : { NAME : 'integer array', ELEMENT_TYPE : TYPE_CODE.INTEGER, NULLABLE : true }, [TYPE_CODE.LONG_ARRAY] : { NAME : 'long array', ELEMENT_TYPE : TYPE_CODE.LONG, NULLABLE : true }, [TYPE_CODE.FLOAT_ARRAY] : { NAME : 'float array', ELEMENT_TYPE : TYPE_CODE.FLOAT, NULLABLE : true }, [TYPE_CODE.DOUBLE_ARRAY] : { NAME : 'double array', ELEMENT_TYPE : TYPE_CODE.DOUBLE, NULLABLE : true }, [TYPE_CODE.CHAR_ARRAY] : { NAME : 'char array', ELEMENT_TYPE : TYPE_CODE.CHAR, NULLABLE : true }, [TYPE_CODE.BOOLEAN_ARRAY] : { NAME : 'boolean array', ELEMENT_TYPE : TYPE_CODE.BOOLEAN, NULLABLE : true }, [TYPE_CODE.STRING_ARRAY] : { NAME : 'string array', ELEMENT_TYPE : TYPE_CODE.STRING, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.DATE_ARRAY] : { NAME : 'date array', ELEMENT_TYPE : TYPE_CODE.DATE, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.UUID_ARRAY] : { NAME : 'UUID array', ELEMENT_TYPE : TYPE_CODE.UUID, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.OBJECT_ARRAY] : { NAME : 'object array', ELEMENT_TYPE : TYPE_CODE.COMPLEX_OBJECT, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.COLLECTION] : { NAME : 'collection', NULLABLE : true }, [TYPE_CODE.MAP] : { NAME : 'map', NULLABLE : true }, [TYPE_CODE.BINARY_OBJECT] : { NAME : 'BinaryObject', NULLABLE : true }, [TYPE_CODE.ENUM] : { NAME : 'enum', NULLABLE : true }, [TYPE_CODE.ENUM_ARRAY] : { NAME : 'enum array', ELEMENT_TYPE : TYPE_CODE.ENUM, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.DECIMAL] : { NAME : 'decimal', NULLABLE : true }, [TYPE_CODE.DECIMAL_ARRAY] : { NAME : 'decimal array', ELEMENT_TYPE : TYPE_CODE.DECIMAL, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.TIMESTAMP] : { NAME : 'timestamp', NULLABLE : true }, [TYPE_CODE.TIMESTAMP_ARRAY] : { NAME : 'timestamp array', ELEMENT_TYPE : TYPE_CODE.TIMESTAMP, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.TIME] : { NAME : 'time', NULLABLE : true }, [TYPE_CODE.TIME_ARRAY] : { NAME : 'time array', ELEMENT_TYPE : TYPE_CODE.TIME, KEEP_ELEMENT_TYPE : true, NULLABLE : true }, [TYPE_CODE.NULL] : { NAME : 'null', NULLABLE : true }, [TYPE_CODE.COMPLEX_OBJECT] : { NAME : 'complex object', NULLABLE : true } }); const UTF8_ENCODING = 'utf8'; class BinaryUtils { static get OPERATION() { return OPERATION; } static get TYPE_CODE() { return TYPE_CODE; } static get TYPE_INFO() { return TYPE_INFO; } static getSize(typeCode) { const size = TYPE_INFO[typeCode].SIZE; return size ? size : 0; } static get ENCODING() { return UTF8_ENCODING; } static getTypeName(type) { if (typeof type === 'string') { return type; } const typeCode = BinaryUtils.getTypeCode(type); return TYPE_INFO[typeCode] ? TYPE_INFO[typeCode].NAME : 'type code ' + typeCode; } static isNullable(type) { return TYPE_INFO[BinaryUtils.getTypeCode(type)].NULLABLE === true; } static getTypeCode(type) { return type instanceof CompositeType ? type._typeCode : type; } static checkObjectType(type, argName) { if (type === null || type instanceof CompositeType) { return; } ArgumentChecker.hasValueFrom(type, argName, false, ObjectType.PRIMITIVE_TYPE); } static calcObjectType(object) { const BinaryObject = require('../BinaryObject'); const objectType = typeof object; if (object === null) { throw Errors.IgniteClientError.unsupportedTypeError(BinaryUtils.TYPE_CODE.NULL); } else if (objectType === 'number') { return BinaryUtils.TYPE_CODE.DOUBLE; } else if (objectType === 'string') { return BinaryUtils.TYPE_CODE.STRING; } else if (objectType === 'boolean') { return BinaryUtils.TYPE_CODE.BOOLEAN; } else if (object instanceof Timestamp) { return BinaryUtils.TYPE_CODE.TIMESTAMP; } else if (object instanceof Date) { return BinaryUtils.TYPE_CODE.DATE; } else if (object instanceof EnumItem) { return BinaryUtils.TYPE_CODE.ENUM; } else if (object instanceof Decimal) { return BinaryUtils.TYPE_CODE.DECIMAL; } else if (object instanceof Array) { if (object.length > 0 && object[0] !== null) { return BinaryUtils.getArrayType(BinaryUtils.calcObjectType(object[0])); } } else if (object instanceof Set) { return new CollectionObjectType(CollectionObjectType.COLLECTION_SUBTYPE.HASH_SET); } else if (object instanceof Map) { return new MapObjectType(); } else if (object instanceof BinaryObject) { return BinaryUtils.TYPE_CODE.BINARY_OBJECT; } else if (objectType === 'object') { return new ComplexObjectType(object); } throw Errors.IgniteClientError.unsupportedTypeError(objectType); } static checkCompatibility(value, type) { if (!type) { return; } const typeCode = BinaryUtils.getTypeCode(type); if (value === null) { if (!BinaryUtils.isNullable(typeCode)) { throw Errors.IgniteClientError.typeCastError(BinaryUtils.TYPE_CODE.NULL, typeCode); } return; } else if (BinaryUtils.isStandardType(typeCode)) { BinaryUtils.checkStandardTypeCompatibility(value, typeCode, type); return; } const valueTypeCode = BinaryUtils.getTypeCode(BinaryUtils.calcObjectType(value)); if (typeCode !== valueTypeCode) { throw Errors.IgniteClientError.typeCastError(valueTypeCode, typeCode); } } static isStandardType(typeCode) { return typeCode !== BinaryUtils.TYPE_CODE.BINARY_OBJECT && typeCode !== BinaryUtils.TYPE_CODE.COMPLEX_OBJECT; } static checkStandardTypeCompatibility(value, typeCode, type = null) { const valueType = typeof value; switch (typeCode) { case BinaryUtils.TYPE_CODE.BYTE: case BinaryUtils.TYPE_CODE.SHORT: case BinaryUtils.TYPE_CODE.INTEGER: case BinaryUtils.TYPE_CODE.LONG: if (!Number.isInteger(value)) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.FLOAT: case BinaryUtils.TYPE_CODE.DOUBLE: if (valueType !== 'number') { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.CHAR: if (valueType !== 'string' || value.length !== 1) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.BOOLEAN: if (valueType !== 'boolean') { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.STRING: if (valueType !== 'string') { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.UUID: if (!value instanceof Array || value.length !== BinaryUtils.getSize(BinaryUtils.TYPE_CODE.UUID)) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } value.forEach(element => BinaryUtils.checkStandardTypeCompatibility(element, BinaryUtils.TYPE_CODE.BYTE)); return; case BinaryUtils.TYPE_CODE.DATE: if (!value instanceof Date) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.ENUM: if (!value instanceof EnumItem) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.DECIMAL: if (!value instanceof Decimal) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.TIMESTAMP: if (!value instanceof Timestamp) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.TIME: if (!value instanceof Date) { throw Errors.IgniteClientError.valueCastError(value, typeCode); } return; case BinaryUtils.TYPE_CODE.BYTE_ARRAY: case BinaryUtils.TYPE_CODE.SHORT_ARRAY: case BinaryUtils.TYPE_CODE.INTEGER_ARRAY: case BinaryUtils.TYPE_CODE.LONG_ARRAY: case BinaryUtils.TYPE_CODE.FLOAT_ARRAY: case BinaryUtils.TYPE_CODE.DOUBLE_ARRAY: case BinaryUtils.TYPE_CODE.CHAR_ARRAY: case BinaryUtils.TYPE_CODE.BOOLEAN_ARRAY: case BinaryUtils.TYPE_CODE.STRING_ARRAY: case BinaryUtils.TYPE_CODE.UUID_ARRAY: case BinaryUtils.TYPE_CODE.DATE_ARRAY: case BinaryUtils.TYPE_CODE.OBJECT_ARRAY: case BinaryUtils.TYPE_CODE.ENUM_ARRAY: case BinaryUtils.TYPE_CODE.DECIMAL_ARRAY: case BinaryUtils.TYPE_CODE.TIMESTAMP_ARRAY: case BinaryUtils.TYPE_CODE.TIME_ARRAY: if (!value instanceof Array) { throw Errors.IgniteClientError.typeCastError(valueType, typeCode); } return; case BinaryUtils.TYPE_CODE.MAP: if (!value instanceof Map) { throw Errors.IgniteClientError.typeCastError(valueType, typeCode); } return; case BinaryUtils.TYPE_CODE.COLLECTION: if (!(type && type._isSet() && value instanceof Set || value instanceof Array)) { throw Errors.IgniteClientError.typeCastError(valueType, type && type._isSet() ? 'set' : typeCode); } return; case BinaryUtils.TYPE_CODE.NULL: if (value !== null) { throw Errors.IgniteClientError.typeCastError('not null', typeCode); } return; default: const valueTypeCode = BinaryUtils.getTypeCode(BinaryUtils.calcObjectType(value)); if (valueTypeCode === BinaryUtils.TYPE_CODE.BINARY_OBJECT) { throw Errors.IgniteClientError.typeCastError(valueTypeCode, typeCode); } return; } } static checkTypesComatibility(expectedType, actualTypeCode) { if (expectedType === null) { return; } const expectedTypeCode = BinaryUtils.getTypeCode(expectedType); if (actualTypeCode === BinaryUtils.TYPE_CODE.NULL) { return; } else if (expectedTypeCode === BinaryUtils.TYPE_CODE.BINARY_OBJECT || actualTypeCode === BinaryUtils.TYPE_CODE.BINARY_OBJECT && expectedTypeCode === BinaryUtils.TYPE_CODE.COMPLEX_OBJECT) { return; } else if (expectedTypeCode === BinaryUtils.TYPE_CODE.ENUM && actualTypeCode === BinaryUtils.TYPE_CODE.BINARY_ENUM) { return; } else if (actualTypeCode !== expectedTypeCode) { throw Errors.IgniteClientError.typeCastError(actualTypeCode, expectedTypeCode); } } static getArrayElementType(arrayType) { if (arrayType instanceof ObjectArrayType) { return arrayType._elementType; } else if (arrayType === BinaryUtils.TYPE_CODE.OBJECT_ARRAY) { return null; } const elementTypeCode = TYPE_INFO[arrayType].ELEMENT_TYPE; if (!elementTypeCode) { throw Errors.IgniteClientError.internalError(); } return elementTypeCode; } static getArrayType(elementType) { switch (BinaryUtils.getTypeCode(elementType)) { case BinaryUtils.TYPE_CODE.BYTE: return BinaryUtils.TYPE_CODE.BYTE_ARRAY; case BinaryUtils.TYPE_CODE.SHORT: return BinaryUtils.TYPE_CODE.SHORT_ARRAY; case BinaryUtils.TYPE_CODE.INTEGER: return BinaryUtils.TYPE_CODE.INTEGER_ARRAY; case BinaryUtils.TYPE_CODE.LONG: return BinaryUtils.TYPE_CODE.LONG_ARRAY; case BinaryUtils.TYPE_CODE.FLOAT: return BinaryUtils.TYPE_CODE.FLOAT_ARRAY; case BinaryUtils.TYPE_CODE.DOUBLE: return BinaryUtils.TYPE_CODE.DOUBLE_ARRAY; case BinaryUtils.TYPE_CODE.CHAR: return BinaryUtils.TYPE_CODE.CHAR_ARRAY; case BinaryUtils.TYPE_CODE.BOOLEAN: return BinaryUtils.TYPE_CODE.BOOLEAN_ARRAY; case BinaryUtils.TYPE_CODE.STRING: return BinaryUtils.TYPE_CODE.STRING_ARRAY; case BinaryUtils.TYPE_CODE.UUID: return BinaryUtils.TYPE_CODE.UUID_ARRAY; case BinaryUtils.TYPE_CODE.DATE: return BinaryUtils.TYPE_CODE.DATE_ARRAY; case BinaryUtils.TYPE_CODE.ENUM: return BinaryUtils.TYPE_CODE.ENUM_ARRAY; case BinaryUtils.TYPE_CODE.DECIMAL: return BinaryUtils.TYPE_CODE.DECIMAL_ARRAY; case BinaryUtils.TYPE_CODE.TIMESTAMP: return BinaryUtils.TYPE_CODE.TIMESTAMP_ARRAY; case BinaryUtils.TYPE_CODE.TIME: return BinaryUtils.TYPE_CODE.TIME_ARRAY; case BinaryUtils.TYPE_CODE.BINARY_OBJECT: return new ObjectArrayType(); default: return new ObjectArrayType(elementType); } } static keepArrayElementType(arrayTypeCode) { return TYPE_INFO[arrayTypeCode].KEEP_ELEMENT_TYPE === true; } static getJsObjectFieldNames(jsObject) { var fields = new Array(); for (let field in jsObject) { if (typeof jsObject[field] !== 'function') { fields.push(field); } } return fields; } static async hashCode(object, communicator, typeCode = null) { if (typeCode === null) { typeCode = BinaryUtils.getTypeCode(BinaryUtils.calcObjectType(object)); } if (BinaryUtils.isStandardType(typeCode)) { return BinaryUtils.standardHashCode(object, typeCode); } else { return await object._getHashCode(communicator); } } // Calculates hash code for an object of a standard type static standardHashCode(object, typeCode = null) { if (typeCode === null) { typeCode = BinaryUtils.getTypeCode(BinaryUtils.calcObjectType(object)); } switch (typeCode) { case BinaryUtils.TYPE_CODE.BYTE: case BinaryUtils.TYPE_CODE.SHORT: case BinaryUtils.TYPE_CODE.INTEGER: return this.intHashCode(object); case BinaryUtils.TYPE_CODE.LONG: return this.longHashCode(object); case BinaryUtils.TYPE_CODE.FLOAT: return this.floatHashCode(object); case BinaryUtils.TYPE_CODE.DOUBLE: return this.doubleHashCode(object); case BinaryUtils.TYPE_CODE.CHAR: return this.charHashCode(object); case BinaryUtils.TYPE_CODE.BOOLEAN: return this.boolHashCode(object); case BinaryUtils.TYPE_CODE.STRING: return this.strHashCode(object); case BinaryUtils.TYPE_CODE.UUID: return this.uuidHashCode(object); case BinaryUtils.TYPE_CODE.TIME: return this.timeHashCode(object); case BinaryUtils.TYPE_CODE.DATE: case BinaryUtils.TYPE_CODE.TIMESTAMP: return this.datetimeHashCode(object); default: return 0; } } static contentHashCode(buffer, startPos, endPos) { let hash = 1; for (let i = startPos; i <= endPos; i++) { hash = 31 * hash + buffer._buffer[i]; hash |= 0; // Convert to 32bit integer } return hash; } static strHashCode(str) { // This method calcuates hash code for the String GridGain type // bool must be a js 'string' let hash = 0, char; if (str && str.length > 0) { for (let i = 0; i < str.length; i++) { char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash |= 0; // Convert to 32bit integer } } return hash; } static strHashCodeLowerCase(str) { return BinaryUtils.strHashCode(str ? str.toLowerCase() : str); } static charHashCode(char) { // This method calcuates hash code for the Char GridGain type // char must be a js 'string' of length 1 return char.charCodeAt(0); } static intHashCode(int) { // This method calcuates hash code for Byte, Short or Integer GridGain types // int must be a js 'number' return int; } static longHashCode(long) { // This method calcuates hash code for the Long GridGain type // long must be a js 'number' const longObj = Long.fromNumber(long); return longObj.getLowBits() ^ longObj.getHighBits(); } static boolHashCode(bool) { // This method calcuates hash code for the Boolean GridGain type // bool must be a js 'boolean' return bool ? 1231 : 1237; } static floatHashCode(float) { // This method calcuates hash code for the Float GridGain type // float must be a js 'number' const buf = new ArrayBuffer(4); (new Float32Array(buf))[0] = float; const int32arr = new Int32Array(buf); return int32arr[0]; } static doubleHashCode(double) { // This method calcuates hash code for the Double GridGain type // double must be a js 'number' const buf = new ArrayBuffer(8); (new Float64Array(buf))[0] = double; const uint32arr = new Uint32Array(buf); return uint32arr[0] ^ uint32arr[1]; } static uuidHashCode(uuid) { // This method calcuates hash code for the UUID GridGain type // uuid must be a js Array of 'number' of length 16 const buf = Buffer.from(uuid); let xor = 0; for (let i = 0; i < 16; i += 4) { xor ^= buf.readUInt32BE(i); } return xor; } static timeHashCode(time) { // This method calcuates hash code for the Time GridGain type // time must be an instance of Date const midnight = new Date(time); midnight.setHours(0, 0, 0, 0); const totalmsec = time.getTime() - midnight.getTime(); return BinaryUtils.longHashCode(totalmsec); } static datetimeHashCode(date) { // This method calcuates hash code for the Timestamp and Date GridGain types // date must be an instance of Date or Timestamp return BinaryUtils.longHashCode(date.getTime()); } } module.exports = BinaryUtils;