"use strict";
const rust = require("../../index");
const BigDecimal = require("./big-decimal");
const Uuid = require("./uuid");
const TimeUuid = require("./time-uuid");
const Duration = require("./duration");
const LocalTime = require("./local-time");
const InetAddress = require("./inet-address");
const LocalDate = require("./local-date");
const { bigintToLong } = require("../new-utils");
const Row = require("./row");
const Tuple = require("./tuple");
const { convertComplexType } = require("./cql-utils");
/**
* Maps the value returned from the Rust driver into expected JS object, based on the provided type information.
*
* @param {rust.CqlValueWrapper} field
* @param {{code: number, info: *|Object}} typ
* @returns {any}
*/
function getCqlObject(field, typ) {
switch (true) {
case field === null:
return null;
case field instanceof rust.LocalDateWrapper:
return LocalDate.fromRust(field);
case field instanceof rust.DurationWrapper:
return Duration.fromRust(field);
case field instanceof rust.InetAddressWrapper:
return InetAddress.fromRust(field);
case field instanceof rust.LocalTimeWrapper:
return LocalTime.fromRust(field);
}
let res;
let value = field;
switch (typ.code) {
case rust.CqlType.Int:
case rust.CqlType.SmallInt:
case rust.CqlType.TinyInt:
case rust.CqlType.Text:
case rust.CqlType.Blob:
case rust.CqlType.Boolean:
case rust.CqlType.Ascii:
case rust.CqlType.Double:
case rust.CqlType.Float:
return value;
case rust.CqlType.BigInt:
return bigintToLong(value);
case rust.CqlType.Counter:
return value;
case rust.CqlType.Decimal:
return BigDecimal.fromBuffer(value);
case rust.CqlType.Timestamp:
// Currently only values inside Date safe range are supported
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#the_epoch_timestamps_and_invalid_date
// The same problem exists in Datastax driver. This probably should be fixed at some point,
// but it's unlikely someone will need timestamps almost 300.000 years into the future.
return new Date(Number(value));
case rust.CqlType.Map:
res = {};
for (const keyValuePair of value) {
res[getCqlObject(keyValuePair[0], typ.info[0])] = getCqlObject(
keyValuePair[1],
typ.info[1],
);
}
return res;
case rust.CqlType.Timeuuid:
return TimeUuid.fromRust(value);
case rust.CqlType.Tuple:
return Tuple.fromArray(
value.map((element, index) =>
element === null
? undefined
: getCqlObject(element, typ.info[index]),
),
);
case rust.CqlType.Uuid:
return Uuid.fromRust(value);
case rust.CqlType.Set:
case rust.CqlType.List:
return value.map((v) => getCqlObject(v, typ.info));
case rust.CqlType.UserDefinedType:
// Considering an Object is just a dictionary, we map here from a Dict<Key, Value> to Dict<Key, getCqlObject(Value)>
// Dictionary elements are not yet converted values returned from Rust,
// so we need to recursively convert those elements into expected types
return Object.fromEntries(
Object.entries(value).map(([key, elem], index) => {
return [
key,
getCqlObject(elem, typ.info.fields[index].type),
];
}),
);
case rust.CqlType.Varint:
return value;
default:
throw new Error(`Unexpected type (${typ})`);
}
}
/**
* Simple way of getting results from rust driver.
* Call the driver O(columns * rows) times
* @param {rust.QueryResultWrapper} result
* @returns {Array<Row> | undefined} Returns array of rows if ResultWrapper has any, and undefined otherwise
*/
function getRowsFromResultsWrapper(result) {
let rustRows = result.getRows();
if (rustRows == null) {
// Empty results are treated as undefined
return undefined;
}
let colNames = result.getColumnsNames();
let types = result.getColumnsTypes().map((typ) => convertComplexType(typ));
let rows = [];
for (let i = 0; i < rustRows.length; i++) {
let cols = rustRows[i];
let collectedRow = new Row(colNames);
for (let j = 0; j < cols.length; j++) {
// Driver returns column names, metadata and values in the same order.
collectedRow[colNames[j]] = getCqlObject(cols[j], types[j]);
}
rows.push(collectedRow);
}
return rows;
}
/**
*
* @param {rust.QueryResultWrapper} result
* @returns {Array.<{name, type}>}
*/
function getColumnsMetadata(result) {
let res = [];
let columnsWrapper = result.getColumnsSpecs();
// TODO: Here, we ask for column type again, despite already requesting that info at the value deserialization
// While this provides some overhead, this is an overhead in requesting metadata, which we do not focus on optimizing
// (and this endpoint is lazy - meaning it's not called in the benchmarks)
let columnsTypes = result
.getColumnsTypes()
.map((typ) => convertComplexType(typ));
for (let i = 0; i < columnsWrapper.length; i++) {
let e = columnsWrapper[i];
res.push({
ksname: e.ksname,
tablename: e.tablename,
name: e.name,
type: columnsTypes[i],
});
}
return res;
}
module.exports.getCqlObject = getCqlObject;
module.exports.getRowsFromResultsWrapper = getRowsFromResultsWrapper;
module.exports.getColumnsMetadata = getColumnsMetadata;