Skip to content

Commit

Permalink
Support protocol version 0.12 (#164)
Browse files Browse the repository at this point in the history
- Addjust codec types to support protocol version 0.12.
- Pin node 16 version because the latest node 16 breaks jest 
  nodejs/node#40030
  • Loading branch information
renawolford6 committed Sep 9, 2021
1 parent 70edb8c commit 5822331
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 17 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/tests.yml
Expand Up @@ -14,19 +14,19 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
node-version: ["12", "13", "14", "15", "16"]
node-version: ["12", "13", "14", "15", "16.8"]
os: [ubuntu-latest]
edgedb-version: ["stable"]

include:
- os: ubuntu-latest
node-version: "16"
node-version: "16.8"
edgedb-version: "nightly"
- os: macos-latest
node-version: "16"
node-version: "16.8"
edgedb-version: "stable"
- os: windows-latest
node-version: "16"
node-version: "16.8"
edgedb-version: "stable"

steps:
Expand Down
42 changes: 32 additions & 10 deletions src/client.ts
Expand Up @@ -34,6 +34,8 @@ import {Set} from "./datatypes/set";
import LRU from "./lru";
import {EMPTY_TUPLE_CODEC, EmptyTupleCodec, TupleCodec} from "./codecs/tuple";
import {NamedTupleCodec} from "./codecs/namedtuple";
import {ObjectCodec} from "./codecs/object";
import {NULL_CODEC, NullCodec} from "./codecs/codecs";
import {
ALLOW_MODIFICATIONS,
INNER,
Expand Down Expand Up @@ -61,7 +63,7 @@ import {
import {Transaction as LegacyTransaction} from "./legacy_transaction";
import {Transaction, START_TRANSACTION_IMPL} from "./transaction";

const PROTO_VER: ProtocolVersion = [0, 11];
const PROTO_VER: ProtocolVersion = [0, 12];
const PROTO_VER_MIN: ProtocolVersion = [0, 9];

enum AuthenticationStatuses {
Expand Down Expand Up @@ -1222,16 +1224,32 @@ export class ConnectionImpl {
}

private _encodeArgs(args: QueryArgs, inCodec: ICodec): Buffer {
if (inCodec === EMPTY_TUPLE_CODEC && !args) {
return EmptyTupleCodec.BUFFER;
}
if (versionGreaterThanOrEqual(this.protocolVersion, [0, 12])) {
if (inCodec === NULL_CODEC && !args) {
return NullCodec.BUFFER;
}

if (inCodec instanceof NamedTupleCodec || inCodec instanceof TupleCodec) {
return inCodec.encodeArgs(args);
}
if (inCodec instanceof ObjectCodec) {
return inCodec.encodeArgs(args);
}

// Shouldn't ever happen.
throw new Error("invalid input codec");
// Shouldn't ever happen.
throw new Error("invalid input codec");
} else {
if (inCodec === EMPTY_TUPLE_CODEC && !args) {
return EmptyTupleCodec.BUFFER;
}

if (
inCodec instanceof NamedTupleCodec ||
inCodec instanceof TupleCodec
) {
return inCodec.encodeArgs(args);
}

// Shouldn't ever happen.
throw new Error("invalid input codec");
}
}

protected async _executeFlow(
Expand Down Expand Up @@ -1573,9 +1591,13 @@ export class RawConnection extends ConnectionImpl {

public async rawExecute(encodedArgs: Buffer | null = null): Promise<Buffer> {
const result = new WriteBuffer();
let inCodec = EMPTY_TUPLE_CODEC;
if (versionGreaterThanOrEqual(this.protocolVersion, [0, 12])) {
inCodec = NULL_CODEC;
}
await this._executeFlow(
encodedArgs, // arguments
EMPTY_TUPLE_CODEC, // inCodec -- to encode lack of arguments.
inCodec, // inCodec -- to encode lack of arguments.
EMPTY_TUPLE_CODEC, // outCodec -- does not matter, it will not be used.
result
);
Expand Down
1 change: 1 addition & 0 deletions src/codecs/codecs.ts
Expand Up @@ -45,6 +45,7 @@ import {KNOWN_TYPENAMES, NULL_CODEC_ID} from "./consts";
///////////////////////////////////////////////////////////////////////////////

export class NullCodec extends Codec implements ICodec {
static BUFFER: Buffer = new WriteBuffer().writeInt32(0).unwrap();
encode(_buf: WriteBuffer, _object: any): void {
throw new Error("null codec cannot used to encode data");
}
Expand Down
6 changes: 6 additions & 0 deletions src/codecs/consts.ts
Expand Up @@ -52,3 +52,9 @@ export const KNOWN_TYPENAMES = (() => {
}
return res;
})();

export const NO_RESULT = 0x6e;
export const AT_MOST_ONE = 0x6f;
export const ONE = 0x41;
export const MANY = 0x6d;
export const AT_LEAST_ONE = 0x4d;
112 changes: 111 additions & 1 deletion src/codecs/object.ts
Expand Up @@ -18,6 +18,7 @@

import {ICodec, Codec, uuid, CodecKind} from "./ifaces";
import {ReadBuffer, WriteBuffer} from "../buffer";
import {ONE, AT_LEAST_ONE} from "./consts";
import {
generateType,
ObjectConstructor,
Expand All @@ -27,9 +28,17 @@ import {
export class ObjectCodec extends Codec implements ICodec {
private codecs: ICodec[];
private names: string[];
private namesSet: Set<string>;
private cardinalities: number[];
private objectType: ObjectConstructor;

constructor(tid: uuid, codecs: ICodec[], names: string[], flags: number[]) {
constructor(
tid: uuid,
codecs: ICodec[],
names: string[],
flags: number[],
cards: number[]
) {
super(tid);

this.codecs = codecs;
Expand All @@ -43,13 +52,114 @@ export class ObjectCodec extends Codec implements ICodec {
}
}
this.names = newNames;
this.namesSet = new Set(newNames);
this.cardinalities = cards;
this.objectType = generateType(newNames, flags);
}

encode(_buf: WriteBuffer, _object: any): void {
throw new Error("Objects cannot be passed as arguments");
}

encodeArgs(args: any): Buffer {
if (this.names[0] === "0") {
return this._encodePositionalArgs(args);
}
return this._encodeNamedArgs(args);
}

_encodePositionalArgs(args: any): Buffer {
if (!Array.isArray(args)) {
throw new Error("an array of arguments was expected");
}

const codecs = this.codecs;
const codecsLen = codecs.length;

if (args.length !== codecsLen) {
throw new Error(
`expected ${codecsLen} argument${codecsLen === 1 ? "" : "s"}, got ${
args.length
}`
);
}

const elemData = new WriteBuffer();
for (let i = 0; i < codecsLen; i++) {
elemData.writeInt32(0); // reserved
const arg = args[i];
if (arg == null) {
const card = this.cardinalities[i];
if (card === ONE || card === AT_LEAST_ONE) {
throw new Error(
`argument ${this.names[i]} is required, but received ${arg}`
);
}
elemData.writeInt32(-1);
} else {
const codec = codecs[i];
codec.encode(elemData, arg);
}
}

const elemBuf = elemData.unwrap();
const buf = new WriteBuffer();
buf.writeInt32(4 + elemBuf.length);
buf.writeInt32(codecsLen);
buf.writeBuffer(elemBuf);
return buf.unwrap();
}

_encodeNamedArgs(args: any): Buffer {
if (args == null) {
throw new Error(
"a named arguments was expected, got a null value instead"
);
}

const keys = Object.keys(args);
const names = this.names;
const namesSet = this.namesSet;
const codecs = this.codecs;
const codecsLen = codecs.length;

if (keys.length > codecsLen) {
const extraKeys = keys.filter((key) => !namesSet.has(key));
throw new Error(
`unexpected named argument${
extraKeys.length === 1 ? "" : "s"
}: "${extraKeys.join('", "')}"`
);
}

const elemData = new WriteBuffer();
for (let i = 0; i < codecsLen; i++) {
const key = names[i];
const val = args[key];

elemData.writeInt32(0); // reserved bytes
if (val == null) {
const card = this.cardinalities[i];
if (card === ONE || card === AT_LEAST_ONE) {
throw new Error(
`argument ${this.names[i]} is required, but received ${val}`
);
}
elemData.writeInt32(-1);
} else {
const codec = codecs[i];
codec.encode(elemData, val);
}
}

const elemBuf = elemData.unwrap();
const buf = new WriteBuffer();
buf.writeInt32(4 + elemBuf.length);
buf.writeInt32(codecsLen);
buf.writeBuffer(elemBuf);
return buf.unwrap();
}

decode(buf: ReadBuffer): any {
const codecs = this.codecs;
const names = this.names;
Expand Down
8 changes: 6 additions & 2 deletions src/codecs/registry.ts
Expand Up @@ -317,14 +317,17 @@ export class CodecsRegistry {
const codecs: ICodec[] = new Array(els);
const names: string[] = new Array(els);
const flags: number[] = new Array(els);
const cards: number[] = new Array(els);

for (let i = 0; i < els; i++) {
let flag: number;
let card: number;
if (versionGreaterThanOrEqual(protocolVersion, [0, 11])) {
flag = frb.readUInt32();
frb.discard(1); // cardinality
card = frb.readUInt8(); // cardinality
} else {
flag = frb.readUInt8();
card = 0;
}

const strLen = frb.readUInt32();
Expand All @@ -339,9 +342,10 @@ export class CodecsRegistry {
codecs[i] = subCodec;
names[i] = name;
flags[i] = flag;
cards[i] = card;
}

res = new ObjectCodec(tid, codecs, names, flags);
res = new ObjectCodec(tid, codecs, names, flags, cards);
break;
}

Expand Down

0 comments on commit 5822331

Please sign in to comment.