Skip to content

Commit

Permalink
feat(types): add most of type supports for the driver (#48)
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Boll <[email protected]>
  • Loading branch information
Daniel-Boll authored Nov 2, 2024
1 parent 6fbe40f commit 1513fad
Show file tree
Hide file tree
Showing 30 changed files with 1,089 additions and 165 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ scylla = { version = "0.13.1", features = [
uuid = { version = "1.10", features = ["serde", "v4", "fast-rng"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
openssl = { version = "0.10.66", features = ["vendored"] }
openssl = { version = "0.10", features = ["vendored"] }

[build-dependencies]
napi-build = "2"
Expand Down
22 changes: 22 additions & 0 deletions examples/custom-types/bigint.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Cluster } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

console.log(`Connecting to ${nodes}`);

const cluster = new Cluster({ nodes });
const session = await cluster.connect();

await session.execute(
"CREATE KEYSPACE IF NOT EXISTS bigints WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
);
await session.useKeyspace("bigints");

await session.execute(
"CREATE TABLE IF NOT EXISTS bigints (a bigint, primary key (a))",
);

await session.execute("INSERT INTO bigints (a) VALUES (?)", [1238773128n]);

const results = await session.execute("SELECT a FROM bigints");
console.log(results);
22 changes: 22 additions & 0 deletions examples/custom-types/floats.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Cluster, Float } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

console.log(`Connecting to ${nodes}`);

const cluster = new Cluster({ nodes });
const session = await cluster.connect();

await session.execute(
"CREATE KEYSPACE IF NOT EXISTS floats WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
);
await session.useKeyspace("floats");

await session.execute(
"CREATE TABLE IF NOT EXISTS floats (a float, primary key (a))",
);

await session.execute("INSERT INTO floats (a) VALUES (?)", [new Float(1.1)]);

const results = await session.execute("SELECT a FROM floats");
console.log(results);
26 changes: 26 additions & 0 deletions examples/custom-types/list.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Cluster, List, Uuid } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

console.log(`Connecting to ${nodes}`);

const cluster = new Cluster({ nodes });
const session = await cluster.connect();

await session.execute(
"CREATE KEYSPACE IF NOT EXISTS lists WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
);
await session.useKeyspace("lists");

await session.execute(
"CREATE TABLE IF NOT EXISTS lists (a uuid, b list<int>, primary key (a))",
);

// NOTE: driver is not throwing errors if the return of the function is not used.
await session.execute("INSERT INTO lists (a, b) VALUES (?, ?)", [
Uuid.randomV4(),
new List<number>([1, 2, 3]),
]);

const results = await session.execute("SELECT * FROM lists");
console.log(results);
29 changes: 29 additions & 0 deletions examples/custom-types/map.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Cluster, Map, Uuid } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

console.log(`Connecting to ${nodes}`);

const cluster = new Cluster({ nodes });
const session = await cluster.connect();

await session.execute(
"CREATE KEYSPACE IF NOT EXISTS maps WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
);
await session.useKeyspace("maps");

await session.execute(
"CREATE TABLE IF NOT EXISTS maps (a uuid, b map<text, int>, primary key (a))",
);

await session.execute("INSERT INTO maps (a, b) VALUES (?, ?)", [
Uuid.randomV4(),
new Map<string, number>([
["a", 1],
["b", 2],
["c", 3],
]),
]);

const results = await session.execute("SELECT * FROM maps");
console.log(results);
25 changes: 25 additions & 0 deletions examples/custom-types/set.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Cluster, Set, Uuid } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

console.log(`Connecting to ${nodes}`);

const cluster = new Cluster({ nodes });
const session = await cluster.connect();

await session.execute(
"CREATE KEYSPACE IF NOT EXISTS sets WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
);
await session.useKeyspace("sets");

await session.execute(
"CREATE TABLE IF NOT EXISTS sets (a uuid, b set<int>, primary key (a))",
);

await session.execute("INSERT INTO sets (a, b) VALUES (?, ?)", [
Uuid.randomV4(),
new Set<number>([1, 2, 3, 1]),
]);

const results = await session.execute("SELECT * FROM sets");
console.log(results);
Empty file added examples/custom-types/tuple.mts
Empty file.
2 changes: 1 addition & 1 deletion examples/udt.mts → examples/custom-types/udt.mts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cluster } from "../index.js";
import { Cluster } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

Expand Down
22 changes: 22 additions & 0 deletions examples/custom-types/uuid.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Cluster, Uuid } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

console.log(`Connecting to ${nodes}`);

const cluster = new Cluster({ nodes });
const session = await cluster.connect();

await session.execute(
"CREATE KEYSPACE IF NOT EXISTS uuids WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
);
await session.useKeyspace("uuids");

await session.execute(
"CREATE TABLE IF NOT EXISTS uuids (a uuid, primary key (a))",
);

await session.execute("INSERT INTO uuids (a) VALUES (?)", [Uuid.randomV4()]);

const results = await session.execute("SELECT a FROM uuids");
console.log(results);
24 changes: 24 additions & 0 deletions examples/custom-types/varint.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Cluster, Varint } from "../../index.js";

const nodes = process.env.CLUSTER_NODES?.split(",") ?? ["127.0.0.1:9042"];

console.log(`Connecting to ${nodes}`);

const cluster = new Cluster({ nodes });
const session = await cluster.connect();

await session.execute(
"CREATE KEYSPACE IF NOT EXISTS varints WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }",
);
await session.useKeyspace("varints");

await session.execute(
"CREATE TABLE IF NOT EXISTS varints (a varint, primary key (a))",
);

await session.execute("INSERT INTO varints (a) VALUES (?)", [
new Varint([0x00, 0x01, 0x02]),
]);

const results = await session.execute("SELECT a FROM varints");
console.log(results);
4 changes: 2 additions & 2 deletions examples/tracing.mts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ console.log(`Connecting to ${nodes}`);
const cluster = new Cluster({ nodes });
const session = await cluster.connect();

const { tracing } = await session.executeWithTracing(
const { tracing, result } = await session.executeWithTracing(
"SELECT * FROM system_schema.scylla_tables",
[],
// {
// prepare: true,
// },
);

console.log(tracing);
console.log(result, tracing);
100 changes: 96 additions & 4 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,41 @@ export declare class Cluster {
}
export type ScyllaCluster = Cluster

export declare class Decimal {
constructor(intVal: Array<number>, scale: number)
/** Returns the string representation of the Decimal. */
toString(): string
}

export declare class Duration {
months: number
days: number
nanoseconds: number
constructor(months: number, days: number, nanoseconds: number)
/** Returns the string representation of the Duration. */
toString(): string
}

/**
* A float number.
*
* Due to the nature of numbers in JavaScript, it's hard to distinguish between integers and floats, so this type is used to represent
* float numbers while any other JS number will be treated as an integer. (This is not the case for BigInts, which are always treated as BigInts).
*/
export declare class Float {
constructor(inner: number)
}

/** A list of any CqlType */
export declare class List<T = NativeTypes> {
constructor(values: T[])
}

/** A map of any CqlType to any CqlType */
export declare class Map<T = NativeTypes, U = NativeTypes> {
constructor(values: Array<Array<T | U>>)
}

export declare class Metrics {
/** Returns counter for nonpaged queries */
getQueriesNum(): bigint
Expand Down Expand Up @@ -76,7 +111,7 @@ export declare class ScyllaClusterData {
export declare class ScyllaSession {
metrics(): Metrics
getClusterData(): Promise<ScyllaClusterData>
executeWithTracing(query: string | Query | PreparedStatement, parameters?: Array<number | string | Uuid | Record<string, number | string | Uuid>> | undefined | null, options?: QueryOptions | undefined | null): Promise<any>
executeWithTracing(query: string | Query | PreparedStatement, parameters?: Array<ParameterWithMapType> | undefined | null, options?: QueryOptions | undefined | null): Promise<TracingReturn>
/**
* Sends a query to the database and receives a response.\
* Returns only a single page of results, to receive multiple pages use (TODO: Not implemented yet)
Expand All @@ -93,8 +128,8 @@ export declare class ScyllaSession {
* driver does not check it by itself, so incorrect data will be written if the order is
* wrong.
*/
execute(query: string | Query | PreparedStatement, parameters?: Array<number | string | Uuid | Record<string, number | string | Uuid>> | undefined | null, options?: QueryOptions | undefined | null): Promise<any>
query(scyllaQuery: Query, parameters?: Array<number | string | Uuid | Record<string, number | string | Uuid>> | undefined | null): Promise<any>
execute(query: string | Query | PreparedStatement, parameters?: Array<ParameterWithMapType> | undefined | null, options?: QueryOptions | undefined | null): Promise<JSQueryResult>
query(scyllaQuery: Query, parameters?: Array<ParameterWithMapType> | undefined | null): Promise<JSQueryResult>
prepare(query: string): Promise<PreparedStatement>
/**
* Perform a batch query\
Expand Down Expand Up @@ -133,7 +168,7 @@ export declare class ScyllaSession {
* console.log(await session.execute("SELECT * FROM users"));
* ```
*/
batch(batch: BatchStatement, parameters: Array<Array<number | string | Uuid | Record<string, number | string | Uuid>> | undefined | null>): Promise<any>
batch(batch: BatchStatement, parameters: Array<Array<ParameterWithMapType> | undefined | null>): Promise<JSQueryResult>
/**
* Sends `USE <keyspace_name>` request on all connections\
* This allows to write `SELECT * FROM table` instead of `SELECT * FROM keyspace.table`\
Expand Down Expand Up @@ -204,6 +239,11 @@ export declare class ScyllaSession {
checkSchemaAgreement(): Promise<boolean>
}

/** A list of any CqlType */
export declare class Set<T = NativeTypes> {
constructor(values: T[])
}

export declare class Uuid {
/** Generates a random UUID v4. */
static randomV4(): Uuid
Expand All @@ -213,6 +253,27 @@ export declare class Uuid {
toString(): string
}

/**
* Native CQL `varint` representation.
*
* Represented as two's-complement binary in big-endian order.
*
* This type is a raw representation in bytes. It's the default
* implementation of `varint` type - independent of any
* external crates and crate features.
*
* # DB data format
* Notice that constructors don't perform any normalization
* on the provided data. This means that underlying bytes may
* contain leading zeros.
*
* Currently, Scylla and Cassandra support non-normalized `varint` values.
* Bytes provided by the user via constructor are passed to DB as is.
*/
export declare class Varint {
constructor(inner: Array<number>)
}

export interface Auth {
username: string
password: string
Expand Down Expand Up @@ -320,3 +381,34 @@ export declare const enum VerifyMode {
None = 0,
Peer = 1
}

type NativeTypes = number | string | Uuid | bigint | Duration | Decimal | Float | List;
type WithMapType = NativeTypes | Record<string, NativeTypes> | NativeTypes[];
type ParameterWithMapType = WithMapType;
type JSQueryResult = Record<string, WithMapType>[];
type TracingReturn = { result: JSQueryResult; tracing: TracingInfo };

export interface TracingInfo {
client?: string; // IP address as a string
command?: string;
coordinator?: string; // IP address as a string
duration?: number;
parameters?: Record<string, string>;
request?: string;
/**
* started_at is a timestamp - time since unix epoch
*/
started_at?: string;
events: TracingEvent[];
}

/**
* A single event happening during a traced query
*/
export interface TracingEvent {
event_id: string;
activity?: string;
source?: string; // IP address as a string
source_elapsed?: number;
thread?: string;
}
13 changes: 10 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -365,19 +365,26 @@ module.exports.BatchStatement = nativeBinding.BatchStatement
module.exports.ScyllaBatchStatement = nativeBinding.ScyllaBatchStatement
module.exports.Cluster = nativeBinding.Cluster
module.exports.ScyllaCluster = nativeBinding.ScyllaCluster
module.exports.Decimal = nativeBinding.Decimal
module.exports.Duration = nativeBinding.Duration
module.exports.Float = nativeBinding.Float
module.exports.List = nativeBinding.List
module.exports.Map = nativeBinding.Map
module.exports.Metrics = nativeBinding.Metrics
module.exports.PreparedStatement = nativeBinding.PreparedStatement
module.exports.Query = nativeBinding.Query
module.exports.ScyllaClusterData = nativeBinding.ScyllaClusterData
module.exports.ScyllaSession = nativeBinding.ScyllaSession
module.exports.Set = nativeBinding.Set
module.exports.Uuid = nativeBinding.Uuid
module.exports.Varint = nativeBinding.Varint
module.exports.Compression = nativeBinding.Compression
module.exports.Consistency = nativeBinding.Consistency
module.exports.SerialConsistency = nativeBinding.SerialConsistency
module.exports.VerifyMode = nativeBinding.VerifyMode

const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom')

nativeBinding.Uuid.prototype[customInspectSymbol] = function () {
return this.toString();
}
nativeBinding.Uuid.prototype[customInspectSymbol] = function () { return this.toString(); }
nativeBinding.Duration.prototype[customInspectSymbol] = function () { return this.toString(); }
nativeBinding.Decimal.prototype[customInspectSymbol] = function () { return this.toString(); }
Loading

0 comments on commit 1513fad

Please sign in to comment.