/*- * Copyright (c) 2018, 2026 Oracle and/or its affiliates. All rights reserved. * * Licensed under the Universal Permissive License v 1.0 as shown at * https://oss.oracle.com/licenses/upl/ */ import type { Config } from "./config"; /** * NoSQL database allows you to store arbitrary precision decimal numbers * in tables by using database type Number, which supports all * numbers represented by Java BigDecimal class. By default, the driver * represents them as Javascript numbers. However this has some limitations: * *

* Because Javascript and Node.js do not currently have a standard way to * represent arbitrary precision decimal numbers, the driver allows you to use * a 3rd party number library of your choice. Typically the number libraries * represent numbers as objects of certain prototype or class and have methods * to perform arithmetic and other operations. If you enable this feature, * the driver will represent the column values of datatype Number as * objects of this type from the number library. You can pass these objects * as record fields for put operations, as key fields for get operations and * they will also be returned as part of get and query results wherever the * column value or expression result is of datatype Number. Then * your application can use methods from the number library to perform further * numeric operations on number objects or convert them to suitable display * representation. *

* In most cases you only need the following: *

    *
  1. Install the 3rd party number library of your choice (typically from * NPM) globally or as a dependency of your application (note that the driver * does not automatically install number libraries)
  2. *
  3. In the initial {@link Config} used to create {@link NoSQLClient}, * specify dbNumber property as dbNumber: 'module_name' * where module_name is the module name of the number library
  4. *
* See examples below. *

* The above is sufficient for most cases when using default settings and * for number libraries that provide standard method names for common * operations. The driver has been tested with the following number * libraries: *

*

* These libraries should work out of the box with simple steps as described * above. However you may use any number library of your choice as long as * it uses objects to represent arbitrary precision decimal numbers, provides * constructor function to create these objects and supports some common * operations. Below we describe what the driver needs to know from the * number library and whether it will work out of the box as mentioned * above or if it may require additional configuration. *

* The driver needs to know the following: *

    *
  1. Constructor function. The constructor should be able to create * the number object from its string representation. Typically the * number libraries export the constructor as a sole module export, so that * the result of require('module_name') is the * constructor. In this case no additional configuration is required. *
  2. *
  3. Conversion to string. Typically this is the instance method * toString (overriden from Object class), in which case no * additional configuration is required.
  4. *
  5. Operations for comparison, addition, subtraction, multiplication and * division, as may be needed by the driver for client-side query processing. * These are typically static (properties of the constructor itself) * and/or instance methods (properties of constructor's prototype), with the * static methods taking 2 arguments and instance * methods taking one argument. The driver will look for common method names * (instance or static) such as "add", "plus" for addition, "subtract", "sub", * "minus" for subtraction, etc. See {@link NumberLibMethods} for complete * list of names. If the number library of your choice uses any of the common * names for each operation listed above, no additional configuration is * required.
  6. *
  7. Precision and rounding mode. These are optional and if not specified, * default values will be used. On the server side, NoSQL database uses Java * BigDecimal to do calculations for Number datatype during query processing. * The result of each operation is rounded accoring to Java MathContext * settings, which consist of precision (number of significant digits) and * rounding mode (how to round the result to precision), so these settings * need to be provided to the server for query processing. You may choose to * provide them explicitly in configuration (see {@link NumberLibConfig}) or * let the driver figure them out from the number library settings using some * common property names (see {@link NumberLibPrecision} and * {@link NumberLibRoundingMode}). If not found, default precision of 20 and * rounding mode ROUND_HALF_UP will be used.
  8. *
*

* Note that configuration for this feature is always specified as * dbNumber property of {@link Config} and thus can be provided * either as part of {@link Config} Javascript object or in JSON file * containing {@link Config}. *

* dbNumber property can be one of the following: *

*/ export type DBNumberConfig = string|DBNumberConstructor|NumberLibConfig; /** * Constructor signature for 3rd party library number type. */ export interface DBNumberConstructor { new (val: string): unknown; } /** * This object can be set as {@link Config#dbNumber} property * to specify configuration for using 3rd party number libary when additional * information is required, as described in {@link DBNumberConfig}. *

* The only required property is {@link Constructor}. As menitoned, * static and instance properties are only needed when * the number library is not using common method names, or if you need to * customize implementation, see {@link NumberLibMethods}. *

* An important note about precision and rounding: *

* Different number libraries have different options to round the results * of arithmetic and other operations. For example, some libraries like * decimal.js and decimal.js-light round to precision, which * is total number of significant digits in the number. These libraries * typically have a precision value specified as configuration setting in the * constructor. Other libraries, like bignumber.js and * big.js round to scale, or decimal places, which is number of * digits after decimal point. These libraries typically have decimal * places value as configuration setting in the constructor. In addition, * some libraries, like decimal.js and bignumber.js do * automatic rounding of some arithmetic operations and other libraries like * big.js and decimal.js-light allow only manual rounding * via methods such as round. *

* The above may have implication on some query results. On the server side, * NoSQL database uses Java BigDecimal and rounds each arithmetic operation * according to precision and rounding mode settings of MathContext, as * mentioned in {@link DBNumberConfig}. The driver, on the other hand, does * not perform rounding during query processing, other than the rounding * performed automatically by some number libraries as mentioned above. This * means that for queries that use arithmetic expressions and some aggregate * functions, the query results may slightly differ when using different * number libraries. From the libraries tested, decimal.js has the * closest matching behavior to query processing on the server side because * it automatically rounds arithmetic operations to precision using rounding * mode setting. *

* You may also use the rounding methods from the number library to perform * rounding after receiving query results. This may be useful when using * number libraries that do not perform automatic rounding. In addition, * number libraries may have other settings related to rounding. See * documentation for the number library of your choice. If required, you can * clone and customize the constructor and pass it as * {@link Constructor} property. You may also customize * arithmetic operations further using static and instance * properties, see {@link NumberLibMethods}, in case non-standard behavior is * needed. *

* The driver need to know precision and rounding mode in order to create * MathContext for server-side query processing. It will try to infer these * settings from the constructor as described in {@link NumberLibPrecision} * and {@link NumberLibRoundingMode}. For libraries that round to scale, * such as bignumber.js and big.js, it is not possible * to infer precision from constructor, so you may need to set * {@link precision} property, otherwise the driver will use the default * value. For {@link precision} and {@link roundingMode} properties, if * set, the driver will use their values instead of inferring them from * the constructor. *

* Note that setting properties {@link precision} and {@link roundingMode} * only affects how rounding is done on the server side. The driver will not * set any properties of the constructor. If the number library does * automatic rounding, it will do so based on the constructor as passed. If * the number library supports precision and rounding mode settings and does * automatic rounding/trucation, you can ensure that the same settings are * used by the server and the client by letting the driver infer them from * the constructor (by using {@link getPrecision} and * {@link getRoundingMode} properties if necessary). E.g. for libraries * decimal.js and decimal.js-light the driver will * automatically infer and use precision and rounding mode settings. */ export interface NumberLibConfig { /** * String representing number library * module name, if the sole export is the constructor, or constructor * function. Constructor function must be able to create instances from * number's string representation or from Javascript number (although * other options may also be provided by the number library). Note the * upper case to disambiguate from Object's constructor property. */ Constructor: string|DBNumberConstructor; /** * In rare cases when the number library export * is not the constructor, you may specify the module name. If this * property is set and {@link Constructor} property is specified as a * string, then the {@link Constructor} property specifies property name * of the module.exports object instead of the module name. */ module?: string; /** * Static method mappings, that is * methods that are properties of the constructor itself. If not set, or * for any required method not present in the mapping, the driver will try * to infer it from constructor. See {@link NumberLibMethods}. */ static?: NumberLibMethods; /** * Instance method mappings, that is * methods that are properties of the constructor's prototype. If not * set, or for any required method not present in the mapping, the driver * will try to infer it from constructor's prototype. See * {@link NumberLibMethods}. */ instance?: NumberLibMethods; /** * Precision to use for rounding of * server-side query calculations on datatype Number. If not * set, the driver will try to infer the value from constructor (see * {@link NumberLibPrecision}). If cannot be inferred, default precision * of 20 is used. */ precision?: number; /** * Rounding mode to use for rounding of * server-side query calculations on datatype Number. See * {@link RoundingModesMap} for details on supported roundings modes. * This property can be specified either as rounding mode name string, * such as 'DOWN', 'UP', 'HALF_DOWN', etc. (with or without * ROUND_ prefix) or as number library-specific constant value, * as long as the driver can find a mapping between rounding mode names * and their values in the number library (see {@link RoundingModesMap}). * If not set, the driver will try to infer the value from constructor * (see {@link NumberLibRoundingMode}). If cannot be inferred, default * value of ROUND_HALF_UP is used. */ roundingMode?: unknown; /** * Specifies how to get * precision value from constructor. See {@link NumberLibPrecision}. If * not set, the driver will use {@link NumberLibConfig#precision}. */ getPrecision?: NumberLibPrecision; /** * Specifies how to get * rounding mode from constructor. See {@link NumberLibRoundingMode}. If * not set, the driver will use {@link NumberLibConfig#roundingMode}. */ getRoundingMode?: NumberLibRoundingMode; /** * Specifies mapping between * rounding mode names and their constant values in the number library. * If not set, the driver will try to infer the mapping from constuctor or * module (see {@link RoundingModesMap}). */ RoundingModes?: RoundingModesMap | string; } /** * Object that specifies method mappings for methods from the number library * that are required by the driver. Set this as * {@link NumberLibConfig#static} property for mappings to static methods * (properties of the constructor) and to {@link NumberLibConfig#instance} * property for mappings to instance methods (properties of the constructor's * prototype). You may also set both of the above to provide static mappings * for some methods and instance mappings for others. For any required * method, one mapping is sufficient (otherwise the driver will prefer * instance over static). *

* Unless otherwise specified, the required operations are binary (i.e. * they operate on two number objects). This means that static methods take 2 * parameters and instance methods have this context as the 1st * number object and pass 2nd number as one parameter. *

* You can specify each method mapping as either string or a function. If * string, the driver will use constructor's or prototype's property by that * name for static or instance method respectively. You may also provide a * function for either static or instance method (as long as it follows the * rules for arguments and this context described in previous * paragraph). This allows you to customize implementation if needed. *

*

* The following assumptions can be made about the argument types: *

* All properties are optional. For any method mapping property, if * not set, the driver will look for methods with list of names as specified. * Unless otherwise specified, the driver will look first for each * instance method on that list and then for each static. If the mapping is * not specified and none of the methods on the list could be found, * {@link NoSQLArgumentError} will result. *

* The above means that you only need to set mappings for methods with * non-typical names that would not be found on corresponding name list or if * you with to customize thier implementation. In particular, for 4 tested * number libraries mentioned in {@link DBNumberConfig}, no mappings are * required. */ export interface NumberLibMethods { /** * Compare two numbers n1 and n2. * Return value of this function should be > 0 if n1 > n2, = 0 if n1 = n2 * and < 0 if n1 < n2. If not set, the driver will look for methods named * comparedTo, compareTo, cmp and * compare in that order */ compare?: string|((n1: any, n2: any) => number); /** * Determine if two numbers are * equal. Should return true/false. This property may be useful for * equality checks during query processing which is usually faster than * numeric comparison via {@link compare}. If not set, * the driver will look for instance methods equals, * isEqualTo and eq in that order. If not found, the * driver will use {@link compare} for equality checks. */ valuesEqual?: string|((n1: any, n2: any) => boolean); /** * Add two numbers n1 and n2, return number * object with value of n1 + n2. If not set, the driver will look for * methods named plus and add in that order. */ add?: string|((n1: any, n2: any) => any); /** * Subtract number n2 from n1, return * number object with value of n1 - n2. If not set, the driver will look * for methods named minus, sub and subtract in * that order. */ subtract?: string|((n1: any, n2: any) => any); /** * Multiply numbers n1 and n2, return * number object with value n1 * n2. If not set, the driver will look for * methods named times, multipliedBy, multiply * and mul in that order. */ multiply?: string|((n1: any, n2: any) => any); /** * Divide number n1 by n2, return number * object with value of n1 / n2. If not set, the driver will look for * methods named dividedBy, divide and div in * that order. */ divide?: string|((n1: any, n2: any) => any); } /** * Value of {@link NumberLibConfig#getPrecision} property. * This property may be: *

* If {@link NumberLibConfig#getPrecision} is not set, the driver will use * value {@link NumberLibConfig#precision} property. If that is also not set, * the driver will check constructor properties precision and * PRECISION. If not found, default precision of 20 is assumed. */ export type NumberLibPrecision = string | ((cons: DBNumberConstructor) => number); /** * Value of of {@link NumberLibConfig#getRoundingMode} property. *

* This property may be: *

* If {@link NumberLibConfig#getRoundingMode} is not set, the driver will use * value {@link NumberLibConfig#roundingMode} property. If that is also not * set, the driver will check constructor properties rounding, * roundingMode, ROUNDING_MODE and RM in that * order. If not found, default rounding mode ROUND_HALF_UP is assumed. *

* Also see {@link RoundingModesMap} for explanation on rounding modes. */ export type NumberLibRoundingMode = string | ((cons: DBNumberConstructor) => unknown); /** * Specifies constant values used for rounding modes in the number library. *

* Rounding mode specifies rounding behavior of numerical and/or rounding * operations in the number library, specifically indicating how the least * significant digit of the result should be calculated. There are several * well known rounding modes. NoSQL database supports rounding modes * used by Java BigDecimal and specified as * {@link https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/math/RoundingMode.html | RoundingMode} * enumeration. Also see documentation for the number library of your choice * on supported rounding modes. *

* This object may be specified as {@link NumberLibConfig#RoundingModes} * property and it helps the driver determine the rounding mode constant * values (which are usually Javascript numbers) for different rounding modes * used by the number library. The driver uses it to infer which rounding * mode is used by the library or set as {@link NumberLibConfig#roundingMode} * property, see {@link NumberLibRoundingMode}. *

* In most cases, it is not necessary to set * {@link NumberLibConfig#RoundingModes} and thus use this object. * Specifically, this property is not necessary if: *

* If not set, the driver will try to infer rounding mode constants by: *
    *
  1. Looking for an object containing these constants by checking properties * RoundingModes and RoundingMode of the constructor and of * the module (if specified). If not found assume the constructor itself * contains the constants.
  2. *
  3. Looking for properties with rounding mode names such as * ROUND_UP, ROUND_DOWN, etc. in the object determined * above, with and without ROUND_ prefix.
  4. *
*

* Note that you may also set {@link NumberLibConfig#RoundingModes} to string * in which case it will be used as a property name or path ('.'s allowed) of * constructor or module to the object containing rounding mode constants * instead of checking candidate names as described above. *

* The properties listed correspond to each rounding mode supported * by the driver. Note that some number libraries, such as big.js, * support only a subset of these. In this case the driver will infer all it * can. If neither {@link NumberLibConfig#RoundingModes} nor * {@link NumberLibConfig#roundingMode} properties are set and rounding mode * constants cannot be inferred, the driver will assume default rounding mode * ROUND_HALF_UP. *

* Note that some number libraries may support rounding modes not listed here * and not supported by Java BigDecimal, such as ROUND_HALF_CEIL and * ROUND_HALF_FLOOR. Using of these rounding modes will result in * {@link NoSQLArgumentError}. *

* Although numeric rounding mode values are usually used, if the library * of your choice using other type for rounding mode constants you may specify * them as well. Any value is allowed except undefined and * null. */ export interface RoundingModesMap { /** * Constant value for ROUND_UP rounding mode. */ UP?: unknown; /** * Constant value for ROUND_DOWN rounding mode. */ DOWN?: unknown; /** * Constant value for ROUND_CEILING rounding mode. */ CEILING?: unknown; /** * Constant value for ROUND_FLOOR rounding mode. */ FLOOR?: unknown; /** * Constant value for ROUND_HALF_UP rounding mode. */ HALF_UP?: unknown; /** * Constant value for ROUND_HALF_DOWN rounding mode. */ HALF_DOWN?: unknown; /** * Constant value for ROUND_HALF_EVEN rounding mode. */ HALF_EVEN?: unknown; }