diff --git a/.github/workflows/rust-cubestore-master.yml b/.github/workflows/rust-cubestore-master.yml index 325251283cf84..b4deccbab7e6a 100644 --- a/.github/workflows/rust-cubestore-master.yml +++ b/.github/workflows/rust-cubestore-master.yml @@ -213,7 +213,7 @@ jobs: if: ${{ startsWith(matrix.os, 'windows') }} run: choco install -y --force llvm --version 18.1.6 - name: Set Env Variables for Windows - uses: allenevans/set-env@v3.0.0 + uses: allenevans/set-env@v4.0.0 if: ${{ startsWith(matrix.os, 'windows') }} with: OPENSSL_DIR: 'C:/vcpkg/packages/openssl_x64-windows' diff --git a/.github/workflows/rust-cubestore.yml b/.github/workflows/rust-cubestore.yml index 509e6b92a4ebe..c76e7e8c0f444 100644 --- a/.github/workflows/rust-cubestore.yml +++ b/.github/workflows/rust-cubestore.yml @@ -152,7 +152,7 @@ jobs: if: ${{ startsWith(matrix.os, 'windows') }} run: choco install -y --force llvm --version 18.1.6 - name: Set Env Variables for Windows - uses: allenevans/set-env@v3.0.0 + uses: allenevans/set-env@v4.0.0 if: ${{ startsWith(matrix.os, 'windows') }} with: OPENSSL_DIR: 'C:/vcpkg/packages/openssl_x64-windows' diff --git a/packages/cubejs-backend-shared/src/env.ts b/packages/cubejs-backend-shared/src/env.ts index b02ce5ee0a235..7f9a8bcaeab93 100644 --- a/packages/cubejs-backend-shared/src/env.ts +++ b/packages/cubejs-backend-shared/src/env.ts @@ -2011,6 +2011,9 @@ const variables: Record any> = { livePreview: () => get('CUBEJS_LIVE_PREVIEW') .default('true') .asBoolStrict(), + cubestoreSendableParameters: () => get('CUBEJS_CUBESTORE_SENDABLE_PARAMETERS') + .default('false') + .asBoolStrict(), externalDefault: () => get('CUBEJS_EXTERNAL_DEFAULT') .default('true') .asBoolStrict(), diff --git a/packages/cubejs-cubestore-driver/codegen/binary-value.ts b/packages/cubejs-cubestore-driver/codegen/binary-value.ts new file mode 100644 index 0000000000000..26a9a9033d2fb --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/binary-value.ts @@ -0,0 +1,71 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +export class BinaryValue { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):BinaryValue { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsBinaryValue(bb:flatbuffers.ByteBuffer, obj?:BinaryValue):BinaryValue { + return (obj || new BinaryValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsBinaryValue(bb:flatbuffers.ByteBuffer, obj?:BinaryValue):BinaryValue { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new BinaryValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +v(index: number):number|null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readUint8(this.bb!.__vector(this.bb_pos + offset) + index) : 0; +} + +vLength():number { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0; +} + +vArray():Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? new Uint8Array(this.bb!.bytes().buffer, this.bb!.bytes().byteOffset + this.bb!.__vector(this.bb_pos + offset), this.bb!.__vector_len(this.bb_pos + offset)) : null; +} + +static startBinaryValue(builder:flatbuffers.Builder) { + builder.startObject(1); +} + +static addV(builder:flatbuffers.Builder, vOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, vOffset, 0); +} + +static createVVector(builder:flatbuffers.Builder, data:number[]|Uint8Array):flatbuffers.Offset { + builder.startVector(1, data.length, 1); + for (let i = data.length - 1; i >= 0; i--) { + builder.addInt8(data[i]!); + } + return builder.endVector(); +} + +static startVVector(builder:flatbuffers.Builder, numElems:number) { + builder.startVector(1, numElems, 1); +} + +static endBinaryValue(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + builder.requiredField(offset, 4) // v + return offset; +} + +static createBinaryValue(builder:flatbuffers.Builder, vOffset:flatbuffers.Offset):flatbuffers.Offset { + BinaryValue.startBinaryValue(builder); + BinaryValue.addV(builder, vOffset); + return BinaryValue.endBinaryValue(builder); +} +} diff --git a/packages/cubejs-cubestore-driver/codegen/bool-value.ts b/packages/cubejs-cubestore-driver/codegen/bool-value.ts new file mode 100644 index 0000000000000..5e2771d329dfb --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/bool-value.ts @@ -0,0 +1,48 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +export class BoolValue { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):BoolValue { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsBoolValue(bb:flatbuffers.ByteBuffer, obj?:BoolValue):BoolValue { + return (obj || new BoolValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsBoolValue(bb:flatbuffers.ByteBuffer, obj?:BoolValue):BoolValue { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new BoolValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +v():boolean { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? !!this.bb!.readInt8(this.bb_pos + offset) : false; +} + +static startBoolValue(builder:flatbuffers.Builder) { + builder.startObject(1); +} + +static addV(builder:flatbuffers.Builder, v:boolean) { + builder.addFieldInt8(0, +v, +false); +} + +static endBoolValue(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createBoolValue(builder:flatbuffers.Builder, v:boolean):flatbuffers.Offset { + BoolValue.startBoolValue(builder); + BoolValue.addV(builder, v); + return BoolValue.endBoolValue(builder); +} +} diff --git a/packages/cubejs-cubestore-driver/codegen/float64-value.ts b/packages/cubejs-cubestore-driver/codegen/float64-value.ts new file mode 100644 index 0000000000000..eba0d72f62323 --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/float64-value.ts @@ -0,0 +1,48 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +export class Float64Value { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):Float64Value { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsFloat64Value(bb:flatbuffers.ByteBuffer, obj?:Float64Value):Float64Value { + return (obj || new Float64Value()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsFloat64Value(bb:flatbuffers.ByteBuffer, obj?:Float64Value):Float64Value { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Float64Value()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +v():number { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readFloat64(this.bb_pos + offset) : 0.0; +} + +static startFloat64Value(builder:flatbuffers.Builder) { + builder.startObject(1); +} + +static addV(builder:flatbuffers.Builder, v:number) { + builder.addFieldFloat64(0, v, 0.0); +} + +static endFloat64Value(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createFloat64Value(builder:flatbuffers.Builder, v:number):flatbuffers.Offset { + Float64Value.startFloat64Value(builder); + Float64Value.addV(builder, v); + return Float64Value.endFloat64Value(builder); +} +} diff --git a/packages/cubejs-cubestore-driver/codegen/http-parameter-value.ts b/packages/cubejs-cubestore-driver/codegen/http-parameter-value.ts new file mode 100644 index 0000000000000..e6def427f79bc --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/http-parameter-value.ts @@ -0,0 +1,54 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import { BinaryValue } from './binary-value.js'; +import { BoolValue } from './bool-value.js'; +import { Float64Value } from './float64-value.js'; +import { Int64Value } from './int64-value.js'; +import { NullValue } from './null-value.js'; +import { StringValue } from './string-value.js'; + + +export enum HttpParameterValue { + NONE = 0, + Int64Value = 1, + BoolValue = 2, + StringValue = 3, + BinaryValue = 4, + Float64Value = 5, + NullValue = 6 +} + +export function unionToHttpParameterValue( + type: HttpParameterValue, + accessor: (obj:BinaryValue|BoolValue|Float64Value|Int64Value|NullValue|StringValue) => BinaryValue|BoolValue|Float64Value|Int64Value|NullValue|StringValue|null +): BinaryValue|BoolValue|Float64Value|Int64Value|NullValue|StringValue|null { + switch(HttpParameterValue[type]) { + case 'NONE': return null; + case 'Int64Value': return accessor(new Int64Value())! as Int64Value; + case 'BoolValue': return accessor(new BoolValue())! as BoolValue; + case 'StringValue': return accessor(new StringValue())! as StringValue; + case 'BinaryValue': return accessor(new BinaryValue())! as BinaryValue; + case 'Float64Value': return accessor(new Float64Value())! as Float64Value; + case 'NullValue': return accessor(new NullValue())! as NullValue; + default: return null; + } +} + +export function unionListToHttpParameterValue( + type: HttpParameterValue, + accessor: (index: number, obj:BinaryValue|BoolValue|Float64Value|Int64Value|NullValue|StringValue) => BinaryValue|BoolValue|Float64Value|Int64Value|NullValue|StringValue|null, + index: number +): BinaryValue|BoolValue|Float64Value|Int64Value|NullValue|StringValue|null { + switch(HttpParameterValue[type]) { + case 'NONE': return null; + case 'Int64Value': return accessor(index, new Int64Value())! as Int64Value; + case 'BoolValue': return accessor(index, new BoolValue())! as BoolValue; + case 'StringValue': return accessor(index, new StringValue())! as StringValue; + case 'BinaryValue': return accessor(index, new BinaryValue())! as BinaryValue; + case 'Float64Value': return accessor(index, new Float64Value())! as Float64Value; + case 'NullValue': return accessor(index, new NullValue())! as NullValue; + default: return null; + } +} diff --git a/packages/cubejs-cubestore-driver/codegen/http-parameter.ts b/packages/cubejs-cubestore-driver/codegen/http-parameter.ts new file mode 100644 index 0000000000000..a6165bdaed105 --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/http-parameter.ts @@ -0,0 +1,62 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +import { HttpParameterValue, unionToHttpParameterValue, unionListToHttpParameterValue } from './http-parameter-value.js'; + + +export class HttpParameter { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):HttpParameter { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsHttpParameter(bb:flatbuffers.ByteBuffer, obj?:HttpParameter):HttpParameter { + return (obj || new HttpParameter()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsHttpParameter(bb:flatbuffers.ByteBuffer, obj?:HttpParameter):HttpParameter { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new HttpParameter()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +valueType():HttpParameterValue { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readUint8(this.bb_pos + offset) : HttpParameterValue.NONE; +} + +value(obj:any):any|null { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.__union(obj, this.bb_pos + offset) : null; +} + +static startHttpParameter(builder:flatbuffers.Builder) { + builder.startObject(2); +} + +static addValueType(builder:flatbuffers.Builder, valueType:HttpParameterValue) { + builder.addFieldInt8(0, valueType, HttpParameterValue.NONE); +} + +static addValue(builder:flatbuffers.Builder, valueOffset:flatbuffers.Offset) { + builder.addFieldOffset(1, valueOffset, 0); +} + +static endHttpParameter(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + builder.requiredField(offset, 6) // value + return offset; +} + +static createHttpParameter(builder:flatbuffers.Builder, valueType:HttpParameterValue, valueOffset:flatbuffers.Offset):flatbuffers.Offset { + HttpParameter.startHttpParameter(builder); + HttpParameter.addValueType(builder, valueType); + HttpParameter.addValue(builder, valueOffset); + return HttpParameter.endHttpParameter(builder); +} +} diff --git a/packages/cubejs-cubestore-driver/codegen/http-query.ts b/packages/cubejs-cubestore-driver/codegen/http-query.ts index d8d5efc684a09..d695cefe2611f 100644 --- a/packages/cubejs-cubestore-driver/codegen/http-query.ts +++ b/packages/cubejs-cubestore-driver/codegen/http-query.ts @@ -4,6 +4,7 @@ import * as flatbuffers from 'flatbuffers'; +import { HttpParameter } from './http-parameter.js'; import { HttpTable } from './http-table.js'; @@ -49,8 +50,18 @@ inlineTablesLength():number { return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0; } +parameters(index: number, obj?:HttpParameter):HttpParameter|null { + const offset = this.bb!.__offset(this.bb_pos, 10); + return offset ? (obj || new HttpParameter()).__init(this.bb!.__indirect(this.bb!.__vector(this.bb_pos + offset) + index * 4), this.bb!) : null; +} + +parametersLength():number { + const offset = this.bb!.__offset(this.bb_pos, 10); + return offset ? this.bb!.__vector_len(this.bb_pos + offset) : 0; +} + static startHttpQuery(builder:flatbuffers.Builder) { - builder.startObject(3); + builder.startObject(4); } static addQuery(builder:flatbuffers.Builder, queryOffset:flatbuffers.Offset) { @@ -77,16 +88,33 @@ static startInlineTablesVector(builder:flatbuffers.Builder, numElems:number) { builder.startVector(4, numElems, 4); } +static addParameters(builder:flatbuffers.Builder, parametersOffset:flatbuffers.Offset) { + builder.addFieldOffset(3, parametersOffset, 0); +} + +static createParametersVector(builder:flatbuffers.Builder, data:flatbuffers.Offset[]):flatbuffers.Offset { + builder.startVector(4, data.length, 4); + for (let i = data.length - 1; i >= 0; i--) { + builder.addOffset(data[i]!); + } + return builder.endVector(); +} + +static startParametersVector(builder:flatbuffers.Builder, numElems:number) { + builder.startVector(4, numElems, 4); +} + static endHttpQuery(builder:flatbuffers.Builder):flatbuffers.Offset { const offset = builder.endObject(); return offset; } -static createHttpQuery(builder:flatbuffers.Builder, queryOffset:flatbuffers.Offset, traceObjOffset:flatbuffers.Offset, inlineTablesOffset:flatbuffers.Offset):flatbuffers.Offset { +static createHttpQuery(builder:flatbuffers.Builder, queryOffset:flatbuffers.Offset, traceObjOffset:flatbuffers.Offset, inlineTablesOffset:flatbuffers.Offset, parametersOffset:flatbuffers.Offset):flatbuffers.Offset { HttpQuery.startHttpQuery(builder); HttpQuery.addQuery(builder, queryOffset); HttpQuery.addTraceObj(builder, traceObjOffset); HttpQuery.addInlineTables(builder, inlineTablesOffset); + HttpQuery.addParameters(builder, parametersOffset); return HttpQuery.endHttpQuery(builder); } } diff --git a/packages/cubejs-cubestore-driver/codegen/index.ts b/packages/cubejs-cubestore-driver/codegen/index.ts index 9f4f1752e79d1..1b52b62c90b56 100644 --- a/packages/cubejs-cubestore-driver/codegen/index.ts +++ b/packages/cubejs-cubestore-driver/codegen/index.ts @@ -2,11 +2,19 @@ /* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ +export { BinaryValue } from './binary-value.js'; +export { BoolValue } from './bool-value.js'; +export { Float64Value } from './float64-value.js'; export { HttpColumnValue } from './http-column-value.js'; export { HttpCommand } from './http-command.js'; export { HttpError } from './http-error.js'; export { HttpMessage } from './http-message.js'; +export { HttpParameter } from './http-parameter.js'; +export { HttpParameterValue } from './http-parameter-value.js'; export { HttpQuery } from './http-query.js'; export { HttpResultSet } from './http-result-set.js'; export { HttpRow } from './http-row.js'; export { HttpTable } from './http-table.js'; +export { Int64Value } from './int64-value.js'; +export { NullValue } from './null-value.js'; +export { StringValue } from './string-value.js'; diff --git a/packages/cubejs-cubestore-driver/codegen/int64-value.ts b/packages/cubejs-cubestore-driver/codegen/int64-value.ts new file mode 100644 index 0000000000000..94d458808ddcf --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/int64-value.ts @@ -0,0 +1,48 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +export class Int64Value { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):Int64Value { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsInt64Value(bb:flatbuffers.ByteBuffer, obj?:Int64Value):Int64Value { + return (obj || new Int64Value()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsInt64Value(bb:flatbuffers.ByteBuffer, obj?:Int64Value):Int64Value { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Int64Value()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +v():bigint { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.readInt64(this.bb_pos + offset) : BigInt('0'); +} + +static startInt64Value(builder:flatbuffers.Builder) { + builder.startObject(1); +} + +static addV(builder:flatbuffers.Builder, v:bigint) { + builder.addFieldInt64(0, v, BigInt('0')); +} + +static endInt64Value(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createInt64Value(builder:flatbuffers.Builder, v:bigint):flatbuffers.Offset { + Int64Value.startInt64Value(builder); + Int64Value.addV(builder, v); + return Int64Value.endInt64Value(builder); +} +} diff --git a/packages/cubejs-cubestore-driver/codegen/null-value.ts b/packages/cubejs-cubestore-driver/codegen/null-value.ts new file mode 100644 index 0000000000000..bb4c6afc45446 --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/null-value.ts @@ -0,0 +1,38 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +export class NullValue { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):NullValue { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsNullValue(bb:flatbuffers.ByteBuffer, obj?:NullValue):NullValue { + return (obj || new NullValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsNullValue(bb:flatbuffers.ByteBuffer, obj?:NullValue):NullValue { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new NullValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static startNullValue(builder:flatbuffers.Builder) { + builder.startObject(0); +} + +static endNullValue(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static createNullValue(builder:flatbuffers.Builder):flatbuffers.Offset { + NullValue.startNullValue(builder); + return NullValue.endNullValue(builder); +} +} diff --git a/packages/cubejs-cubestore-driver/codegen/string-value.ts b/packages/cubejs-cubestore-driver/codegen/string-value.ts new file mode 100644 index 0000000000000..90c0a349f6529 --- /dev/null +++ b/packages/cubejs-cubestore-driver/codegen/string-value.ts @@ -0,0 +1,51 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + +export class StringValue { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):StringValue { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsStringValue(bb:flatbuffers.ByteBuffer, obj?:StringValue):StringValue { + return (obj || new StringValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsStringValue(bb:flatbuffers.ByteBuffer, obj?:StringValue):StringValue { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new StringValue()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +v():string|null +v(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +v(optionalEncoding?:any):string|Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +} + +static startStringValue(builder:flatbuffers.Builder) { + builder.startObject(1); +} + +static addV(builder:flatbuffers.Builder, vOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, vOffset, 0); +} + +static endStringValue(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + builder.requiredField(offset, 4) // v + return offset; +} + +static createStringValue(builder:flatbuffers.Builder, vOffset:flatbuffers.Offset):flatbuffers.Offset { + StringValue.startStringValue(builder); + StringValue.addV(builder, vOffset); + return StringValue.endStringValue(builder); +} +} diff --git a/packages/cubejs-cubestore-driver/src/CubeStoreCacheDriver.ts b/packages/cubejs-cubestore-driver/src/CubeStoreCacheDriver.ts index e56188c2e7aab..8e5eb0634be82 100644 --- a/packages/cubejs-cubestore-driver/src/CubeStoreCacheDriver.ts +++ b/packages/cubejs-cubestore-driver/src/CubeStoreCacheDriver.ts @@ -1,12 +1,16 @@ -import { createCancelablePromise, MaybeCancelablePromise } from '@cubejs-backend/shared'; +import { createCancelablePromise, getEnv, MaybeCancelablePromise } from '@cubejs-backend/shared'; import { CacheDriverInterface } from '@cubejs-backend/base-driver'; import { CubeStoreDriver } from './CubeStoreDriver'; export class CubeStoreCacheDriver implements CacheDriverInterface { + protected readonly sendParameters: boolean; + public constructor( protected connectionFactory: () => Promise, - ) {} + ) { + this.sendParameters = getEnv('cubestoreSendableParameters'); + } protected connection: CubeStoreDriver | null = null; @@ -60,9 +64,12 @@ export class CubeStoreCacheDriver implements CacheDriverInterface { }); public async get(key: string) { - const rows = await (await this.getConnection()).query('CACHE GET ?', [ + const connection = await this.getConnection(); + const rows = await connection.query('CACHE GET ?', [ key - ]); + ], { + sendParameters: this.sendParameters && await connection.hasCapability('sendableParameters') + }); if (rows && rows.length === 1) { return JSON.parse(rows[0].value); } @@ -72,7 +79,10 @@ export class CubeStoreCacheDriver implements CacheDriverInterface { public async set(key: string, value, expiration) { const strValue = JSON.stringify(value); - await (await this.getConnection()).query('CACHE SET TTL ? ? ?', [expiration, key, strValue]); + const connection = await this.getConnection(); + await connection.query('CACHE SET TTL ? ? ?', [expiration, key, strValue], { + sendParameters: this.sendParameters && await connection.hasCapability('sendableParameters') + }); return { key, @@ -81,9 +91,10 @@ export class CubeStoreCacheDriver implements CacheDriverInterface { } public async remove(key: string) { - await (await this.getConnection()).query('CACHE REMOVE ?', [ - key - ]); + const connection = await this.getConnection(); + await connection.query('CACHE REMOVE ?', [key], { + sendParameters: this.sendParameters && await connection.hasCapability('sendableParameters') + }); } public async keysStartingWith(prefix: string) { diff --git a/packages/cubejs-cubestore-driver/src/CubeStoreDriver.ts b/packages/cubejs-cubestore-driver/src/CubeStoreDriver.ts index 6b610cfe23b30..43dc60eb2456b 100644 --- a/packages/cubejs-cubestore-driver/src/CubeStoreDriver.ts +++ b/packages/cubejs-cubestore-driver/src/CubeStoreDriver.ts @@ -21,12 +21,12 @@ import fetch from 'node-fetch'; import { ConnectionConfig } from './types'; import { WebSocketConnection } from './WebSocketConnection'; -type CubeStoreCapability = 'queueExclusive' | 'queueExternalId'; - -const CubeStoreCapabilityMinVersion: Record = { +const CubeStoreCapabilityMinVersion = { queueExclusive: '1.6.22', queueExternalId: '1.6.26', -}; + sendableParameters: '1.6.38', +} satisfies Record; +type CubeStoreCapability = keyof typeof CubeStoreCapabilityMinVersion; const GenericTypeToCubeStore: Record = { string: 'varchar(255)', @@ -59,6 +59,10 @@ type CreateTableOptions = { disableQuoting?: boolean }; +type CubeStoreQueryOptions = QueryOptions & { + sendParameters?: boolean, +}; + export class CubeStoreDriver extends BaseDriver implements DriverInterface { protected readonly config: any; @@ -93,10 +97,16 @@ export class CubeStoreDriver extends BaseDriver implements DriverInterface { await this.query('SELECT 1', []); } - public async query(query: string, values: any[], options?: QueryOptions): Promise { - const { inlineTables, ...queryTracingObj } = options ?? {}; - const sql = formatSql(query, values || []); - return this.connection.query(sql, inlineTables ?? [], { ...queryTracingObj, instance: getEnv('instanceId') }); + public async query(query: string, values: any[], options?: CubeStoreQueryOptions): Promise { + const { inlineTables, sendParameters, ...queryTracingObj } = options ?? {}; + + if (!sendParameters) { + query = formatSql(query, values || []); + } + + const tracingObj = { ...queryTracingObj, instance: getEnv('instanceId') }; + + return this.connection.query(query, inlineTables ?? [], tracingObj, sendParameters ? values : undefined); } public async release() { @@ -125,7 +135,7 @@ export class CubeStoreDriver extends BaseDriver implements DriverInterface { withEntries.push(`delimiter = '${options.delimiter}'`); } if (options.disableQuoting) { - withEntries.push(`disable_quoting = true`); + withEntries.push('disable_quoting = true'); } if (options.buildRangeEnd) { withEntries.push(`build_range_end = '${options.buildRangeEnd}'`); diff --git a/packages/cubejs-cubestore-driver/src/CubeStoreQueueDriver.ts b/packages/cubejs-cubestore-driver/src/CubeStoreQueueDriver.ts index 03e5b172f527c..28a8fc0b2a38e 100644 --- a/packages/cubejs-cubestore-driver/src/CubeStoreQueueDriver.ts +++ b/packages/cubejs-cubestore-driver/src/CubeStoreQueueDriver.ts @@ -39,13 +39,16 @@ type CubeStoreListResponse = { }; export class CubestoreQueueDriverConnection implements QueueDriverConnectionInterface { - private readonly externalIdEnabled: boolean; + protected readonly externalIdEnabled: boolean; + + protected readonly sendParameters: boolean; public constructor( protected readonly driver: CubeStoreDriver, protected readonly options: QueueDriverOptions, ) { this.externalIdEnabled = getEnv('queueExternalId'); + this.sendParameters = getEnv('cubestoreSendableParameters'); } public async useExternalId(): Promise { @@ -352,11 +355,13 @@ export class CubestoreQueueDriverConnection implements QueueDriverConnectionInte } public async setResultAndRemoveQuery(hash: QueryKeyHash, executionResult: unknown, _processingId: ProcessingId, queueId: QueueId): Promise { - const rows = await this.driver.query('QUEUE ACK ? ? ', [ + const rows = await this.driver.query('QUEUE ACK ? ?', [ // queryKeyHash as compatibility fallback queueId || this.prefixKey(hash), executionResult ? JSON.stringify(executionResult) : executionResult - ]); + ], { + sendParameters: this.sendParameters && await this.driver.hasCapability('sendableParameters') + }); if (rows && rows.length === 1) { return rows[0].success === 'true'; } diff --git a/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts b/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts index 9d7c8250768bc..d30a4b827666b 100644 --- a/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts +++ b/packages/cubejs-cubestore-driver/src/WebSocketConnection.ts @@ -6,12 +6,19 @@ import { getEnv, getProcessUid } from '@cubejs-backend/shared'; import { parseCubestoreResultMessage } from '@cubejs-backend/native'; import { ConnectionError, QueryError } from './errors'; import { + BinaryValue, + BoolValue, Float64Value, HttpCommand, HttpError, HttpMessage, + HttpParameter, + HttpParameterValue, HttpQuery, HttpResultSet, - HttpTable + HttpTable, + Int64Value, + NullValue, + StringValue, } from '../codegen'; interface SentMessage { @@ -239,13 +246,91 @@ export class WebSocketConnection { }); } - public async query(query: string, inlineTables: InlineTable[], queryTracingObj?: any): Promise { + protected serializeParameter(builder: flatbuffers.Builder, parameter: unknown) { + if (parameter === null || parameter === undefined) { + const httpParameterValueOffset = NullValue.createNullValue(builder); + + return HttpParameter.createHttpParameter( + builder, + HttpParameterValue.NullValue, + httpParameterValueOffset + ); + } + + switch (typeof parameter) { + case 'object': + { + if (Buffer.isBuffer(parameter)) { + const valueOffset = BinaryValue.createVVector(builder, parameter); + const httpParameterValueOffset = BinaryValue.createBinaryValue(builder, valueOffset); + + return HttpParameter.createHttpParameter( + builder, + HttpParameterValue.BinaryValue, + httpParameterValueOffset + ); + } else { + throw new Error('Parameter with type: object is not supported'); + } + } + case 'boolean': + { + const httpParameterValueOffset = BoolValue.createBoolValue( + builder, + parameter + ); + + return HttpParameter.createHttpParameter( + builder, + HttpParameterValue.BoolValue, + httpParameterValueOffset + ); + } + case 'number': + { + if (Number.isInteger(parameter)) { + const httpParameterValueOffset = Int64Value.createInt64Value(builder, BigInt(parameter)); + + return HttpParameter.createHttpParameter( + builder, + HttpParameterValue.Int64Value, + httpParameterValueOffset + ); + } else { + const httpParameterValueOffset = Float64Value.createFloat64Value(builder, parameter); + + return HttpParameter.createHttpParameter( + builder, + HttpParameterValue.Float64Value, + httpParameterValueOffset + ); + } + } + case 'string': + { + const valueOffset = builder.createString(parameter); + const httpParameterValueOffset = StringValue.createStringValue(builder, valueOffset); + + return HttpParameter.createHttpParameter( + builder, + HttpParameterValue.StringValue, + httpParameterValueOffset + ); + } + default: + throw new Error(`Parameter with type: ${typeof parameter} is not supported`); + } + } + + public async query(query: string, inlineTables: InlineTable[], queryTracingObj?: any, parameters?: any): Promise { const builder = new flatbuffers.Builder(1024); const queryOffset = builder.createString(query); + let traceObjOffset: number | null = null; if (queryTracingObj) { traceObjOffset = builder.createString(JSON.stringify(queryTracingObj)); } + let inlineTablesOffset: number | null = null; if (inlineTables && inlineTables.length > 0) { const inlineTableOffsets: number[] = []; @@ -274,14 +359,36 @@ export class WebSocketConnection { } inlineTablesOffset = HttpQuery.createInlineTablesVector(builder, inlineTableOffsets); } + + let parametersOffset: flatbuffers.Offset | null = null; + if (parameters) { + const httpParameterValues: flatbuffers.Offset[] = []; + + for (const parameter of parameters) { + httpParameterValues.push(this.serializeParameter(builder, parameter)); + } + + parametersOffset = HttpQuery.createParametersVector( + builder, + httpParameterValues + ); + } + HttpQuery.startHttpQuery(builder); HttpQuery.addQuery(builder, queryOffset); + if (traceObjOffset) { HttpQuery.addTraceObj(builder, traceObjOffset); } + if (inlineTablesOffset) { HttpQuery.addInlineTables(builder, inlineTablesOffset); } + + if (parametersOffset) { + HttpQuery.addParameters(builder, parametersOffset); + } + const httpQueryOffset = HttpQuery.endHttpQuery(builder); const messageId = this.messageCounter++; const connectionIdOffset = builder.createString(this.connectionId); diff --git a/rust/cubeshared/src/codegen/http_message.fbs b/rust/cubeshared/src/codegen/http_message.fbs index 3dd42082908bf..ad9038a55bfea 100644 --- a/rust/cubeshared/src/codegen/http_message.fbs +++ b/rust/cubeshared/src/codegen/http_message.fbs @@ -10,10 +10,23 @@ table HttpMessage { connection_id: string; } +table Int64Value { v: int64; } +table BoolValue { v: bool; } +table StringValue { v: string (required); } +table BinaryValue { v: [ubyte] (required); } +table Float64Value { v: float64; } +table NullValue { } +union HttpParameterValue { Int64Value, BoolValue, StringValue, BinaryValue, Float64Value, NullValue } + +table HttpParameter { + value: HttpParameterValue (required); +} + table HttpQuery { query: string; trace_obj: string; inline_tables: [HttpTable]; + parameters: [HttpParameter]; } table HttpTable { @@ -41,5 +54,4 @@ table HttpColumnValue { string_value: string; } - root_type HttpMessage; diff --git a/rust/cubeshared/src/codegen/http_message_generated.rs b/rust/cubeshared/src/codegen/http_message_generated.rs index a4ec6b6fe1191..5ae37dccc821c 100644 --- a/rust/cubeshared/src/codegen/http_message_generated.rs +++ b/rust/cubeshared/src/codegen/http_message_generated.rs @@ -106,6 +106,122 @@ impl<'a> ::flatbuffers::Verifiable for HttpCommand { impl ::flatbuffers::SimpleToVerifyInSlice for HttpCommand {} pub struct HttpCommandUnionTableOffset {} +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] +pub const ENUM_MIN_HTTP_PARAMETER_VALUE: u8 = 0; +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] +pub const ENUM_MAX_HTTP_PARAMETER_VALUE: u8 = 6; +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] +#[allow(non_camel_case_types)] +pub const ENUM_VALUES_HTTP_PARAMETER_VALUE: [HttpParameterValue; 7] = [ + HttpParameterValue::NONE, + HttpParameterValue::Int64Value, + HttpParameterValue::BoolValue, + HttpParameterValue::StringValue, + HttpParameterValue::BinaryValue, + HttpParameterValue::Float64Value, + HttpParameterValue::NullValue, +]; + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[repr(transparent)] +pub struct HttpParameterValue(pub u8); +#[allow(non_upper_case_globals)] +impl HttpParameterValue { + pub const NONE: Self = Self(0); + pub const Int64Value: Self = Self(1); + pub const BoolValue: Self = Self(2); + pub const StringValue: Self = Self(3); + pub const BinaryValue: Self = Self(4); + pub const Float64Value: Self = Self(5); + pub const NullValue: Self = Self(6); + + pub const ENUM_MIN: u8 = 0; + pub const ENUM_MAX: u8 = 6; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::NONE, + Self::Int64Value, + Self::BoolValue, + Self::StringValue, + Self::BinaryValue, + Self::Float64Value, + Self::NullValue, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::NONE => Some("NONE"), + Self::Int64Value => Some("Int64Value"), + Self::BoolValue => Some("BoolValue"), + Self::StringValue => Some("StringValue"), + Self::BinaryValue => Some("BinaryValue"), + Self::Float64Value => Some("Float64Value"), + Self::NullValue => Some("NullValue"), + _ => None, + } + } +} +impl ::core::fmt::Debug for HttpParameterValue { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } + } +} +impl<'a> ::flatbuffers::Follow<'a> for HttpParameterValue { + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = unsafe { ::flatbuffers::read_scalar_at::(buf, loc) }; + Self(b) + } +} + +impl ::flatbuffers::Push for HttpParameterValue { + type Output = HttpParameterValue; + #[inline] + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + unsafe { ::flatbuffers::emplace_scalar::(dst, self.0) }; + } +} + +impl ::flatbuffers::EndianScalar for HttpParameterValue { + type Scalar = u8; + #[inline] + fn to_little_endian(self) -> u8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: u8) -> Self { + let b = u8::from_le(v); + Self(b) + } +} + +impl<'a> ::flatbuffers::Verifiable for HttpParameterValue { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + u8::run_verifier(v, pos) + } +} + +impl ::flatbuffers::SimpleToVerifyInSlice for HttpParameterValue {} +pub struct HttpParameterValueUnionTableOffset {} + pub enum HttpMessageOffset {} #[derive(Copy, Clone, PartialEq)] @@ -401,15 +517,15 @@ impl ::core::fmt::Debug for HttpMessage<'_> { ds.finish() } } -pub enum HttpQueryOffset {} +pub enum Int64ValueOffset {} #[derive(Copy, Clone, PartialEq)] -pub struct HttpQuery<'a> { +pub struct Int64Value<'a> { pub _tab: ::flatbuffers::Table<'a>, } -impl<'a> ::flatbuffers::Follow<'a> for HttpQuery<'a> { - type Inner = HttpQuery<'a>; +impl<'a> ::flatbuffers::Follow<'a> for Int64Value<'a> { + type Inner = Int64Value<'a>; #[inline] unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { Self { @@ -418,14 +534,12 @@ impl<'a> ::flatbuffers::Follow<'a> for HttpQuery<'a> { } } -impl<'a> HttpQuery<'a> { - pub const VT_QUERY: ::flatbuffers::VOffsetT = 4; - pub const VT_TRACE_OBJ: ::flatbuffers::VOffsetT = 6; - pub const VT_INLINE_TABLES: ::flatbuffers::VOffsetT = 8; +impl<'a> Int64Value<'a> { + pub const VT_V: ::flatbuffers::VOffsetT = 4; #[inline] pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { - HttpQuery { _tab: table } + Int64Value { _tab: table } } #[allow(unused_mut)] pub fn create< @@ -435,122 +549,1115 @@ impl<'a> HttpQuery<'a> { A: ::flatbuffers::Allocator + 'bldr, >( _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, - args: &'args HttpQueryArgs<'args>, - ) -> ::flatbuffers::WIPOffset> { - let mut builder = HttpQueryBuilder::new(_fbb); - if let Some(x) = args.inline_tables { - builder.add_inline_tables(x); - } - if let Some(x) = args.trace_obj { - builder.add_trace_obj(x); - } - if let Some(x) = args.query { - builder.add_query(x); - } + args: &'args Int64ValueArgs, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = Int64ValueBuilder::new(_fbb); + builder.add_v(args.v); builder.finish() } #[inline] - pub fn query(&self) -> Option<&'a str> { + pub fn v(&self) -> i64 { // Safety: // Created from valid Table for this object // which contains a valid value in this slot - unsafe { - self._tab - .get::<::flatbuffers::ForwardsUOffset<&str>>(HttpQuery::VT_QUERY, None) + unsafe { self._tab.get::(Int64Value::VT_V, Some(0)).unwrap() } + } +} + +impl ::flatbuffers::Verifiable for Int64Value<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("v", Self::VT_V, false)? + .finish(); + Ok(()) + } +} +pub struct Int64ValueArgs { + pub v: i64, +} +impl<'a> Default for Int64ValueArgs { + #[inline] + fn default() -> Self { + Int64ValueArgs { v: 0 } + } +} + +pub struct Int64ValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> Int64ValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_v(&mut self, v: i64) { + self.fbb_.push_slot::(Int64Value::VT_V, v, 0); + } + #[inline] + pub fn new( + _fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + ) -> Int64ValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + Int64ValueBuilder { + fbb_: _fbb, + start_: start, } } #[inline] - pub fn trace_obj(&self) -> Option<&'a str> { - // Safety: - // Created from valid Table for this object - // which contains a valid value in this slot - unsafe { - self._tab - .get::<::flatbuffers::ForwardsUOffset<&str>>(HttpQuery::VT_TRACE_OBJ, None) + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for Int64Value<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("Int64Value"); + ds.field("v", &self.v()); + ds.finish() + } +} +pub enum BoolValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct BoolValue<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for BoolValue<'a> { + type Inner = BoolValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { ::flatbuffers::Table::new(buf, loc) }, } } +} + +impl<'a> BoolValue<'a> { + pub const VT_V: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + BoolValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: ::flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args BoolValueArgs, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = BoolValueBuilder::new(_fbb); + builder.add_v(args.v); + builder.finish() + } + #[inline] - pub fn inline_tables( - &self, - ) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + pub fn v(&self) -> bool { // Safety: // Created from valid Table for this object // which contains a valid value in this slot - unsafe { - self._tab.get::<::flatbuffers::ForwardsUOffset< - ::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>, - >>(HttpQuery::VT_INLINE_TABLES, None) - } + unsafe { self._tab.get::(BoolValue::VT_V, Some(false)).unwrap() } } } -impl ::flatbuffers::Verifiable for HttpQuery<'_> { +impl ::flatbuffers::Verifiable for BoolValue<'_> { #[inline] fn run_verifier( v: &mut ::flatbuffers::Verifier, pos: usize, ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { v.visit_table(pos)? - .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("query", Self::VT_QUERY, false)? - .visit_field::<::flatbuffers::ForwardsUOffset<&str>>( - "trace_obj", - Self::VT_TRACE_OBJ, - false, - )? - .visit_field::<::flatbuffers::ForwardsUOffset< - ::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>, - >>("inline_tables", Self::VT_INLINE_TABLES, false)? + .visit_field::("v", Self::VT_V, false)? .finish(); Ok(()) } } -pub struct HttpQueryArgs<'a> { - pub query: Option<::flatbuffers::WIPOffset<&'a str>>, - pub trace_obj: Option<::flatbuffers::WIPOffset<&'a str>>, - pub inline_tables: Option< - ::flatbuffers::WIPOffset< - ::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>, - >, - >, +pub struct BoolValueArgs { + pub v: bool, } -impl<'a> Default for HttpQueryArgs<'a> { +impl<'a> Default for BoolValueArgs { #[inline] fn default() -> Self { - HttpQueryArgs { - query: None, - trace_obj: None, - inline_tables: None, - } + BoolValueArgs { v: false } } } -pub struct HttpQueryBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { +pub struct BoolValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, } -impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> HttpQueryBuilder<'a, 'b, A> { +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> BoolValueBuilder<'a, 'b, A> { #[inline] - pub fn add_query(&mut self, query: ::flatbuffers::WIPOffset<&'b str>) { - self.fbb_ - .push_slot_always::<::flatbuffers::WIPOffset<_>>(HttpQuery::VT_QUERY, query); + pub fn add_v(&mut self, v: bool) { + self.fbb_.push_slot::(BoolValue::VT_V, v, false); } #[inline] - pub fn add_trace_obj(&mut self, trace_obj: ::flatbuffers::WIPOffset<&'b str>) { - self.fbb_ - .push_slot_always::<::flatbuffers::WIPOffset<_>>(HttpQuery::VT_TRACE_OBJ, trace_obj); + pub fn new( + _fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + ) -> BoolValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + BoolValueBuilder { + fbb_: _fbb, + start_: start, + } } #[inline] - pub fn add_inline_tables( - &mut self, - inline_tables: ::flatbuffers::WIPOffset< - ::flatbuffers::Vector<'b, ::flatbuffers::ForwardsUOffset>>, - >, - ) { - self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>( - HttpQuery::VT_INLINE_TABLES, - inline_tables, - ); + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for BoolValue<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("BoolValue"); + ds.field("v", &self.v()); + ds.finish() + } +} +pub enum StringValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct StringValue<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for StringValue<'a> { + type Inner = StringValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { ::flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> StringValue<'a> { + pub const VT_V: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + StringValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: ::flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args StringValueArgs<'args>, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = StringValueBuilder::new(_fbb); + if let Some(x) = args.v { + builder.add_v(x); + } + builder.finish() + } + + #[inline] + pub fn v(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::<::flatbuffers::ForwardsUOffset<&str>>(StringValue::VT_V, None) + .unwrap() + } + } +} + +impl ::flatbuffers::Verifiable for StringValue<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("v", Self::VT_V, true)? + .finish(); + Ok(()) + } +} +pub struct StringValueArgs<'a> { + pub v: Option<::flatbuffers::WIPOffset<&'a str>>, +} +impl<'a> Default for StringValueArgs<'a> { + #[inline] + fn default() -> Self { + StringValueArgs { + v: None, // required field + } + } +} + +pub struct StringValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> StringValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_v(&mut self, v: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::<::flatbuffers::WIPOffset<_>>(StringValue::VT_V, v); + } + #[inline] + pub fn new( + _fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + ) -> StringValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + StringValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, StringValue::VT_V, "v"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for StringValue<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("StringValue"); + ds.field("v", &self.v()); + ds.finish() + } +} +pub enum BinaryValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct BinaryValue<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for BinaryValue<'a> { + type Inner = BinaryValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { ::flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> BinaryValue<'a> { + pub const VT_V: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + BinaryValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: ::flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args BinaryValueArgs<'args>, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = BinaryValueBuilder::new(_fbb); + if let Some(x) = args.v { + builder.add_v(x); + } + builder.finish() + } + + #[inline] + pub fn v(&self) -> ::flatbuffers::Vector<'a, u8> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'a, u8>>>( + BinaryValue::VT_V, + None, + ) + .unwrap() + } + } +} + +impl ::flatbuffers::Verifiable for BinaryValue<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<::flatbuffers::Vector<'_, u8>>>( + "v", + Self::VT_V, + true, + )? + .finish(); + Ok(()) + } +} +pub struct BinaryValueArgs<'a> { + pub v: Option<::flatbuffers::WIPOffset<::flatbuffers::Vector<'a, u8>>>, +} +impl<'a> Default for BinaryValueArgs<'a> { + #[inline] + fn default() -> Self { + BinaryValueArgs { + v: None, // required field + } + } +} + +pub struct BinaryValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> BinaryValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_v(&mut self, v: ::flatbuffers::WIPOffset<::flatbuffers::Vector<'b, u8>>) { + self.fbb_ + .push_slot_always::<::flatbuffers::WIPOffset<_>>(BinaryValue::VT_V, v); + } + #[inline] + pub fn new( + _fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + ) -> BinaryValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + BinaryValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, BinaryValue::VT_V, "v"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for BinaryValue<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("BinaryValue"); + ds.field("v", &self.v()); + ds.finish() + } +} +pub enum Float64ValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct Float64Value<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for Float64Value<'a> { + type Inner = Float64Value<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { ::flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> Float64Value<'a> { + pub const VT_V: ::flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + Float64Value { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: ::flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args Float64ValueArgs, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = Float64ValueBuilder::new(_fbb); + builder.add_v(args.v); + builder.finish() + } + + #[inline] + pub fn v(&self) -> f64 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Float64Value::VT_V, Some(0.0)).unwrap() } + } +} + +impl ::flatbuffers::Verifiable for Float64Value<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::("v", Self::VT_V, false)? + .finish(); + Ok(()) + } +} +pub struct Float64ValueArgs { + pub v: f64, +} +impl<'a> Default for Float64ValueArgs { + #[inline] + fn default() -> Self { + Float64ValueArgs { v: 0.0 } + } +} + +pub struct Float64ValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> Float64ValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_v(&mut self, v: f64) { + self.fbb_.push_slot::(Float64Value::VT_V, v, 0.0); + } + #[inline] + pub fn new( + _fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + ) -> Float64ValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + Float64ValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for Float64Value<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("Float64Value"); + ds.field("v", &self.v()); + ds.finish() + } +} +pub enum NullValueOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct NullValue<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for NullValue<'a> { + type Inner = NullValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { ::flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> NullValue<'a> { + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + NullValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: ::flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + _args: &'args NullValueArgs, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = NullValueBuilder::new(_fbb); + builder.finish() + } +} + +impl ::flatbuffers::Verifiable for NullValue<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)?.finish(); + Ok(()) + } +} +pub struct NullValueArgs {} +impl<'a> Default for NullValueArgs { + #[inline] + fn default() -> Self { + NullValueArgs {} + } +} + +pub struct NullValueBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> NullValueBuilder<'a, 'b, A> { + #[inline] + pub fn new( + _fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + ) -> NullValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + NullValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for NullValue<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("NullValue"); + ds.finish() + } +} +pub enum HttpParameterOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct HttpParameter<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for HttpParameter<'a> { + type Inner = HttpParameter<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { ::flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> HttpParameter<'a> { + pub const VT_VALUE_TYPE: ::flatbuffers::VOffsetT = 4; + pub const VT_VALUE: ::flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + HttpParameter { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: ::flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args HttpParameterArgs, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = HttpParameterBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + builder.add_value_type(args.value_type); + builder.finish() + } + + #[inline] + pub fn value_type(&self) -> HttpParameterValue { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::( + HttpParameter::VT_VALUE_TYPE, + Some(HttpParameterValue::NONE), + ) + .unwrap() + } + } + #[inline] + pub fn value(&self) -> ::flatbuffers::Table<'a> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::<::flatbuffers::ForwardsUOffset<::flatbuffers::Table<'a>>>( + HttpParameter::VT_VALUE, + None, + ) + .unwrap() + } + } + #[inline] + #[allow(non_snake_case)] + pub fn value_as_int_64_value(&self) -> Option> { + if self.value_type() == HttpParameterValue::Int64Value { + let u = self.value(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { Int64Value::init_from_table(u) }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn value_as_bool_value(&self) -> Option> { + if self.value_type() == HttpParameterValue::BoolValue { + let u = self.value(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { BoolValue::init_from_table(u) }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn value_as_string_value(&self) -> Option> { + if self.value_type() == HttpParameterValue::StringValue { + let u = self.value(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { StringValue::init_from_table(u) }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn value_as_binary_value(&self) -> Option> { + if self.value_type() == HttpParameterValue::BinaryValue { + let u = self.value(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { BinaryValue::init_from_table(u) }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn value_as_float_64_value(&self) -> Option> { + if self.value_type() == HttpParameterValue::Float64Value { + let u = self.value(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { Float64Value::init_from_table(u) }) + } else { + None + } + } + + #[inline] + #[allow(non_snake_case)] + pub fn value_as_null_value(&self) -> Option> { + if self.value_type() == HttpParameterValue::NullValue { + let u = self.value(); + // Safety: + // Created from a valid Table for this object + // Which contains a valid union in this slot + Some(unsafe { NullValue::init_from_table(u) }) + } else { + None + } + } +} + +impl ::flatbuffers::Verifiable for HttpParameter<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_union::( + "value_type", + Self::VT_VALUE_TYPE, + "value", + Self::VT_VALUE, + true, + |key, v, pos| match key { + HttpParameterValue::Int64Value => v + .verify_union_variant::<::flatbuffers::ForwardsUOffset>( + "HttpParameterValue::Int64Value", + pos, + ), + HttpParameterValue::BoolValue => v + .verify_union_variant::<::flatbuffers::ForwardsUOffset>( + "HttpParameterValue::BoolValue", + pos, + ), + HttpParameterValue::StringValue => v + .verify_union_variant::<::flatbuffers::ForwardsUOffset>( + "HttpParameterValue::StringValue", + pos, + ), + HttpParameterValue::BinaryValue => v + .verify_union_variant::<::flatbuffers::ForwardsUOffset>( + "HttpParameterValue::BinaryValue", + pos, + ), + HttpParameterValue::Float64Value => v + .verify_union_variant::<::flatbuffers::ForwardsUOffset>( + "HttpParameterValue::Float64Value", + pos, + ), + HttpParameterValue::NullValue => v + .verify_union_variant::<::flatbuffers::ForwardsUOffset>( + "HttpParameterValue::NullValue", + pos, + ), + _ => Ok(()), + }, + )? + .finish(); + Ok(()) + } +} +pub struct HttpParameterArgs { + pub value_type: HttpParameterValue, + pub value: Option<::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>>, +} +impl<'a> Default for HttpParameterArgs { + #[inline] + fn default() -> Self { + HttpParameterArgs { + value_type: HttpParameterValue::NONE, + value: None, // required field + } + } +} + +pub struct HttpParameterBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> HttpParameterBuilder<'a, 'b, A> { + #[inline] + pub fn add_value_type(&mut self, value_type: HttpParameterValue) { + self.fbb_.push_slot::( + HttpParameter::VT_VALUE_TYPE, + value_type, + HttpParameterValue::NONE, + ); + } + #[inline] + pub fn add_value(&mut self, value: ::flatbuffers::WIPOffset<::flatbuffers::UnionWIPOffset>) { + self.fbb_ + .push_slot_always::<::flatbuffers::WIPOffset<_>>(HttpParameter::VT_VALUE, value); + } + #[inline] + pub fn new( + _fbb: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + ) -> HttpParameterBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + HttpParameterBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> ::flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, HttpParameter::VT_VALUE, "value"); + ::flatbuffers::WIPOffset::new(o.value()) + } +} + +impl ::core::fmt::Debug for HttpParameter<'_> { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut ds = f.debug_struct("HttpParameter"); + ds.field("value_type", &self.value_type()); + match self.value_type() { + HttpParameterValue::Int64Value => { + if let Some(x) = self.value_as_int_64_value() { + ds.field("value", &x) + } else { + ds.field( + "value", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + HttpParameterValue::BoolValue => { + if let Some(x) = self.value_as_bool_value() { + ds.field("value", &x) + } else { + ds.field( + "value", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + HttpParameterValue::StringValue => { + if let Some(x) = self.value_as_string_value() { + ds.field("value", &x) + } else { + ds.field( + "value", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + HttpParameterValue::BinaryValue => { + if let Some(x) = self.value_as_binary_value() { + ds.field("value", &x) + } else { + ds.field( + "value", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + HttpParameterValue::Float64Value => { + if let Some(x) = self.value_as_float_64_value() { + ds.field("value", &x) + } else { + ds.field( + "value", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + HttpParameterValue::NullValue => { + if let Some(x) = self.value_as_null_value() { + ds.field("value", &x) + } else { + ds.field( + "value", + &"InvalidFlatbuffer: Union discriminant does not match value.", + ) + } + } + _ => { + let x: Option<()> = None; + ds.field("value", &x) + } + }; + ds.finish() + } +} +pub enum HttpQueryOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct HttpQuery<'a> { + pub _tab: ::flatbuffers::Table<'a>, +} + +impl<'a> ::flatbuffers::Follow<'a> for HttpQuery<'a> { + type Inner = HttpQuery<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: unsafe { ::flatbuffers::Table::new(buf, loc) }, + } + } +} + +impl<'a> HttpQuery<'a> { + pub const VT_QUERY: ::flatbuffers::VOffsetT = 4; + pub const VT_TRACE_OBJ: ::flatbuffers::VOffsetT = 6; + pub const VT_INLINE_TABLES: ::flatbuffers::VOffsetT = 8; + pub const VT_PARAMETERS: ::flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: ::flatbuffers::Table<'a>) -> Self { + HttpQuery { _tab: table } + } + #[allow(unused_mut)] + pub fn create< + 'bldr: 'args, + 'args: 'mut_bldr, + 'mut_bldr, + A: ::flatbuffers::Allocator + 'bldr, + >( + _fbb: &'mut_bldr mut ::flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args HttpQueryArgs<'args>, + ) -> ::flatbuffers::WIPOffset> { + let mut builder = HttpQueryBuilder::new(_fbb); + if let Some(x) = args.parameters { + builder.add_parameters(x); + } + if let Some(x) = args.inline_tables { + builder.add_inline_tables(x); + } + if let Some(x) = args.trace_obj { + builder.add_trace_obj(x); + } + if let Some(x) = args.query { + builder.add_query(x); + } + builder.finish() + } + + #[inline] + pub fn query(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::<::flatbuffers::ForwardsUOffset<&str>>(HttpQuery::VT_QUERY, None) + } + } + #[inline] + pub fn trace_obj(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::<::flatbuffers::ForwardsUOffset<&str>>(HttpQuery::VT_TRACE_OBJ, None) + } + } + #[inline] + pub fn inline_tables( + &self, + ) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::<::flatbuffers::ForwardsUOffset< + ::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>, + >>(HttpQuery::VT_INLINE_TABLES, None) + } + } + #[inline] + pub fn parameters( + &self, + ) -> Option<::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::<::flatbuffers::ForwardsUOffset< + ::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>, + >>(HttpQuery::VT_PARAMETERS, None) + } + } +} + +impl ::flatbuffers::Verifiable for HttpQuery<'_> { + #[inline] + fn run_verifier( + v: &mut ::flatbuffers::Verifier, + pos: usize, + ) -> Result<(), ::flatbuffers::InvalidFlatbuffer> { + v.visit_table(pos)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>("query", Self::VT_QUERY, false)? + .visit_field::<::flatbuffers::ForwardsUOffset<&str>>( + "trace_obj", + Self::VT_TRACE_OBJ, + false, + )? + .visit_field::<::flatbuffers::ForwardsUOffset< + ::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>, + >>("inline_tables", Self::VT_INLINE_TABLES, false)? + .visit_field::<::flatbuffers::ForwardsUOffset< + ::flatbuffers::Vector<'_, ::flatbuffers::ForwardsUOffset>, + >>("parameters", Self::VT_PARAMETERS, false)? + .finish(); + Ok(()) + } +} +pub struct HttpQueryArgs<'a> { + pub query: Option<::flatbuffers::WIPOffset<&'a str>>, + pub trace_obj: Option<::flatbuffers::WIPOffset<&'a str>>, + pub inline_tables: Option< + ::flatbuffers::WIPOffset< + ::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>, + >, + >, + pub parameters: Option< + ::flatbuffers::WIPOffset< + ::flatbuffers::Vector<'a, ::flatbuffers::ForwardsUOffset>>, + >, + >, +} +impl<'a> Default for HttpQueryArgs<'a> { + #[inline] + fn default() -> Self { + HttpQueryArgs { + query: None, + trace_obj: None, + inline_tables: None, + parameters: None, + } + } +} + +pub struct HttpQueryBuilder<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> { + fbb_: &'b mut ::flatbuffers::FlatBufferBuilder<'a, A>, + start_: ::flatbuffers::WIPOffset<::flatbuffers::TableUnfinishedWIPOffset>, +} +impl<'a: 'b, 'b, A: ::flatbuffers::Allocator + 'a> HttpQueryBuilder<'a, 'b, A> { + #[inline] + pub fn add_query(&mut self, query: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::<::flatbuffers::WIPOffset<_>>(HttpQuery::VT_QUERY, query); + } + #[inline] + pub fn add_trace_obj(&mut self, trace_obj: ::flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::<::flatbuffers::WIPOffset<_>>(HttpQuery::VT_TRACE_OBJ, trace_obj); + } + #[inline] + pub fn add_inline_tables( + &mut self, + inline_tables: ::flatbuffers::WIPOffset< + ::flatbuffers::Vector<'b, ::flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_.push_slot_always::<::flatbuffers::WIPOffset<_>>( + HttpQuery::VT_INLINE_TABLES, + inline_tables, + ); + } + #[inline] + pub fn add_parameters( + &mut self, + parameters: ::flatbuffers::WIPOffset< + ::flatbuffers::Vector<'b, ::flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::<::flatbuffers::WIPOffset<_>>(HttpQuery::VT_PARAMETERS, parameters); } #[inline] pub fn new( @@ -575,6 +1682,7 @@ impl ::core::fmt::Debug for HttpQuery<'_> { ds.field("query", &self.query()); ds.field("trace_obj", &self.trace_obj()); ds.field("inline_tables", &self.inline_tables()); + ds.field("parameters", &self.parameters()); ds.finish() } } diff --git a/rust/cubestore/Cargo.lock b/rust/cubestore/Cargo.lock index edaf976b76b60..45e96d5540ad0 100644 --- a/rust/cubestore/Cargo.lock +++ b/rust/cubestore/Cargo.lock @@ -6034,15 +6034,15 @@ dependencies = [ [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601f9201feb9b09c00266478bf459952b9ef9a6b94edb2f21eba14ab681a60a9" +checksum = "cddb07e32ddb770749da91081d8d0ac3a16f1a569a18b20348cd371f5dead06b" dependencies = [ "cc", "cfg-if 1.0.0", diff --git a/rust/cubestore/cubestore/src/http/mod.rs b/rust/cubestore/cubestore/src/http/mod.rs index 56fd30f21cde7..c081d5fba0f2e 100644 --- a/rust/cubestore/cubestore/src/http/mod.rs +++ b/rust/cubestore/cubestore/src/http/mod.rs @@ -6,7 +6,9 @@ use warp::{Filter, Rejection, Reply}; use crate::metastore::{Column, ColumnType, ImportFormat}; use crate::mysql::SqlAuthService; -use crate::sql::{InlineTable, InlineTables, SqlQueryContext, SqlService}; +use crate::sql::{ + InlineTable, InlineTables, QueryParameter, QueryParameters, SqlQueryContext, SqlService, +}; use crate::store::DataFrame; use crate::table::{Row, TableValue}; use crate::util::WorkerLoop; @@ -14,8 +16,8 @@ use crate::{app_metrics, CubeError}; use async_std::fs::File; use cubeshared::codegen::{ root_as_http_message, HttpColumnValue, HttpColumnValueArgs, HttpError, HttpErrorArgs, - HttpMessageArgs, HttpQuery, HttpQueryArgs, HttpResultSet, HttpResultSetArgs, HttpRow, - HttpRowArgs, + HttpMessageArgs, HttpParameterValue, HttpQuery, HttpQueryArgs, HttpResultSet, + HttpResultSetArgs, HttpRow, HttpRowArgs, }; use cubeshared::flatbuffers::{FlatBufferBuilder, ForwardsUOffset, Vector, WIPOffset}; use datafusion::cube_ext; @@ -126,6 +128,7 @@ impl HttpServer { Ok(user) => Ok(SqlQueryContext { user, inline_tables: InlineTables::new(), + parameters: None, trace_obj: None, process_id, }), @@ -172,12 +175,32 @@ impl HttpServer { } Ok(msg) => { if msg.is_binary() { - match HttpMessage::read(msg.into_bytes()).await { - Err(e) => error!("Websocket message read error: {:?}", e), + let message_buffer = msg.into_bytes(); + let http_message = match root_as_http_message(&message_buffer) { + Err(e) => { + error!("Websocket message deserialization error: {:?}", e); + continue; + }, + Ok(http_message) => http_message, + }; + + let message_id = http_message.message_id(); + let connection_id = http_message.connection_id().map(|s| s.to_string()); + + match HttpMessage::read(http_message).await { + Err(e) => { + error!("Websocket message read error: {:?}", e); + + let send_res = web_socket.send( + Message::binary(HttpMessage { message_id, connection_id, command: HttpCommand::Error { error: e.to_string() } }.bytes()) + ).await; + if let Err(e) = send_res { + error!("Websocket message send error: {:?}", e) + } + break; + }, Ok(msg) => { trace!("Received web socket message (process_id: {})", process_id); - let message_id = msg.message_id; - let connection_id = msg.connection_id.clone(); // TODO use timeout instead of try send for burst control however try_send is safer for now if let Err(e) = tx_to_move.try_send((response_tx.clone(), sql_query_context.clone(), msg)) { error!("Websocket channel error: {:?}", e); @@ -556,12 +579,14 @@ impl HttpServer { query, inline_tables, trace_obj, + parameters, } => Ok(HttpCommand::ResultSet { data_frame: sql_service .exec_query_with_context( sql_query_context .with_trace_obj(trace_obj) - .with_inline_tables(&inline_tables), + .with_inline_tables(&inline_tables) + .with_parameters(¶meters), &query, ) .await?, @@ -614,6 +639,7 @@ pub enum HttpCommand { query: String, inline_tables: InlineTables, trace_obj: Option, + parameters: Option, }, ResultSet { data_frame: Arc, @@ -644,12 +670,19 @@ impl HttpMessage { query, inline_tables, trace_obj, + parameters, } => { let query_offset = builder.create_string(&query); let trace_obj_offset = trace_obj.as_ref().map(|o| builder.create_string(o)); + if !inline_tables.is_empty() { - panic!("Not implemented") + panic!("serializing inline_tables is not implemented") } + + if parameters.is_some() { + panic!("serializing parameters is not implemented") + } + Some( HttpQuery::create( &mut builder, @@ -657,6 +690,7 @@ impl HttpMessage { query: Some(query_offset), inline_tables: None, trace_obj: trace_obj_offset, + parameters: None, }, ) .as_union_value(), @@ -796,14 +830,16 @@ impl HttpMessage { rows } - pub async fn read(buffer: Vec) -> Result { - let http_message = root_as_http_message(buffer.as_slice())?; + pub async fn read<'a>( + http_message: cubeshared::codegen::HttpMessage<'a>, + ) -> Result { Ok(HttpMessage { message_id: http_message.message_id(), connection_id: http_message.connection_id().map(|s| s.to_string()), command: match http_message.command_type() { cubeshared::codegen::HttpCommand::HttpQuery => { let query = http_message.command_as_http_query().unwrap(); + let mut inline_tables = Vec::new(); if let Some(query_inline_tables) = query.inline_tables() { for inline_table in query_inline_tables.iter() { @@ -843,10 +879,54 @@ impl HttpMessage { )); } }; + + let parameters = if let Some(http_params) = query.parameters() { + let mut res = Vec::new(); + + for http_param in http_params.iter() { + let value = match http_param.value_type() { + HttpParameterValue::NullValue => QueryParameter::Null, + HttpParameterValue::Int64Value => QueryParameter::Int64Value( + http_param.value_as_int_64_value().unwrap().v(), + ), + HttpParameterValue::BoolValue => QueryParameter::BoolValue( + http_param.value_as_bool_value().unwrap().v(), + ), + HttpParameterValue::StringValue => QueryParameter::StringValue( + http_param.value_as_string_value().unwrap().v().to_string(), + ), + HttpParameterValue::BinaryValue => QueryParameter::BinaryValue( + http_param + .value_as_binary_value() + .unwrap() + .v() + .iter() + .collect::>(), + ), + HttpParameterValue::Float64Value => QueryParameter::Float64Value( + http_param.value_as_float_64_value().unwrap().v(), + ), + value_type => { + return Err(CubeError::internal(format!( + "Unsupported parameter type: {:?}", + value_type + ))) + } + }; + + res.push(value); + } + + Some(res) + } else { + None + }; + HttpCommand::Query { query: query.query().unwrap().to_string(), - inline_tables, trace_obj: query.trace_obj().map(|q| q.to_string()), + inline_tables, + parameters, } } cubeshared::codegen::HttpCommand::HttpResultSet => { @@ -897,6 +977,7 @@ impl HttpMessage { #[cfg(test)] mod tests { + use super::*; use crate::config::{init_test_logger, Config}; use crate::http::{HttpCommand, HttpMessage, HttpServer}; use crate::metastore::{Column, ColumnType}; @@ -943,11 +1024,14 @@ mod tests { query: "test query".to_string(), inline_tables: vec![], trace_obj: Some("test trace".to_string()), + parameters: None, }, connection_id: Some("foo".to_string()), }; let bytes = message.bytes(); - let output_message = HttpMessage::read(bytes).await.unwrap(); + let output_message = HttpMessage::read(root_as_http_message(&bytes).unwrap()) + .await + .unwrap(); assert_eq!(message, output_message); } @@ -1011,6 +1095,7 @@ mod tests { query: Some(query_offset), inline_tables: Some(inline_tables_offset), trace_obj: None, + parameters: None, }, ); let args = HttpMessageArgs { @@ -1022,7 +1107,9 @@ mod tests { let message = cubeshared::codegen::HttpMessage::create(&mut builder, &args); builder.finish(message, None); let bytes = builder.finished_data().to_vec(); - let message = HttpMessage::read(bytes).await.unwrap(); + let message = HttpMessage::read(root_as_http_message(&bytes).unwrap()) + .await + .unwrap(); assert_eq!( message, HttpMessage { @@ -1034,7 +1121,8 @@ mod tests { "table".to_string(), Arc::new(DataFrame::new(columns, rows.clone())) )], - trace_obj: None + trace_obj: None, + parameters: None }, connection_id: Some("foo".to_string()), } @@ -1148,6 +1236,7 @@ mod tests { query: query.to_string(), inline_tables: vec![], trace_obj: None, + parameters: None, }, connection_id, } @@ -1179,7 +1268,9 @@ mod tests { counter: &str, ) { let msg = socket.next().await.unwrap().unwrap(); - let message = HttpMessage::read(msg.into_data()).await.unwrap(); + let message = HttpMessage::read(root_as_http_message(&msg.into_data()).unwrap()) + .await + .unwrap(); if let HttpCommand::ResultSet { data_frame } = message.command { if let TableValue::String(v) = data_frame .get_rows() diff --git a/rust/cubestore/cubestore/src/mysql/mod.rs b/rust/cubestore/cubestore/src/mysql/mod.rs index a56b381b05835..2b2e054208c73 100644 --- a/rust/cubestore/cubestore/src/mysql/mod.rs +++ b/rust/cubestore/cubestore/src/mysql/mod.rs @@ -64,6 +64,7 @@ impl AsyncMysqlShim for Backend { inline_tables: InlineTables::new(), trace_obj: None, process_id: None, + parameters: None, }, query, ) diff --git a/rust/cubestore/cubestore/src/queryplanner/mod.rs b/rust/cubestore/cubestore/src/queryplanner/mod.rs index d1dc768a3ccea..b34ea6fb67d0d 100644 --- a/rust/cubestore/cubestore/src/queryplanner/mod.rs +++ b/rust/cubestore/cubestore/src/queryplanner/mod.rs @@ -1042,7 +1042,11 @@ pub mod tests { use pretty_assertions::assert_eq; fn initial_plan(s: &str, ctx: MetaStoreSchemaProvider) -> LogicalPlan { - let statement = match CubeStoreParser::new(s).unwrap().parse_statement().unwrap() { + let statement = match CubeStoreParser::new(s, None) + .unwrap() + .parse_statement() + .unwrap() + { Statement::Statement(s) => s, other => panic!("not a statement, actual {:?}", other), }; diff --git a/rust/cubestore/cubestore/src/queryplanner/partition_filter.rs b/rust/cubestore/cubestore/src/queryplanner/partition_filter.rs index a7b3486d84c18..d59b2f396fcf8 100644 --- a/rust/cubestore/cubestore/src/queryplanner/partition_filter.rs +++ b/rust/cubestore/cubestore/src/queryplanner/partition_filter.rs @@ -1456,7 +1456,7 @@ mod tests { fn parse(s: &str, schema: &Schema) -> Expr { let sql_expr; - let parsed = CubeStoreParser::new(&format!("SELECT {}", s)) + let parsed = CubeStoreParser::new(&format!("SELECT {}", s), None) .unwrap() .parse_statement() .unwrap(); diff --git a/rust/cubestore/cubestore/src/queryplanner/planning.rs b/rust/cubestore/cubestore/src/queryplanner/planning.rs index 0fcad901c5983..9093dd220f747 100644 --- a/rust/cubestore/cubestore/src/queryplanner/planning.rs +++ b/rust/cubestore/cubestore/src/queryplanner/planning.rs @@ -2821,7 +2821,11 @@ pub mod tests { } fn initial_plan(s: &str, i: &TestIndices) -> LogicalPlan { - let statement = match CubeStoreParser::new(s).unwrap().parse_statement().unwrap() { + let statement = match CubeStoreParser::new(s, None) + .unwrap() + .parse_statement() + .unwrap() + { Statement::Statement(s) => s, other => panic!("not a statement, actual {:?}", other), }; diff --git a/rust/cubestore/cubestore/src/sql/cachestore.rs b/rust/cubestore/cubestore/src/sql/cachestore.rs index bc4aaee7af5b8..5b66b924f6095 100644 --- a/rust/cubestore/cubestore/src/sql/cachestore.rs +++ b/rust/cubestore/cubestore/src/sql/cachestore.rs @@ -613,11 +613,11 @@ impl SqlService for CacheStoreSqlService { async fn exec_query_with_context( &self, - ctx: SqlQueryContext, + mut ctx: SqlQueryContext, query: &str, ) -> Result, CubeError> { let stmt = { - let mut parser = CubeStoreParser::new(query)?; + let mut parser = CubeStoreParser::new(query, ctx.parameters.take())?; parser.parse_statement()? }; diff --git a/rust/cubestore/cubestore/src/sql/mod.rs b/rust/cubestore/cubestore/src/sql/mod.rs index 3b82ef2d824b2..86e0c10514501 100644 --- a/rust/cubestore/cubestore/src/sql/mod.rs +++ b/rust/cubestore/cubestore/src/sql/mod.rs @@ -134,12 +134,49 @@ impl InlineTable { } } +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +pub enum QueryParameter { + Null, + StringValue(String), + BoolValue(bool), + BinaryValue(Vec), + Int64Value(i64), + Float64Value(f64), +} + +impl QueryParameter { + pub fn get_type(&self) -> &'static str { + match self { + QueryParameter::Null => "null", + QueryParameter::StringValue(_) => "string", + QueryParameter::BoolValue(_) => "bool", + QueryParameter::BinaryValue(_) => "binary", + QueryParameter::Int64Value(_) => "int64", + QueryParameter::Float64Value(_) => "float64", + } + } + + pub fn try_as_u64(&self) -> Result { + match self { + QueryParameter::Int64Value(v) => u64::try_from(*v) + .map_err(|err| format!("value must be a valid unsigned integer, error: {}", err)), + other => Err(format!( + "Wrong parameters type, actual: {}, expected: integer parameter", + other.get_type() + )), + } + } +} + +pub type QueryParameters = Vec; + #[derive(Serialize, Deserialize, Debug, Default, Clone)] pub struct SqlQueryContext { pub user: Option, pub inline_tables: InlineTables, pub trace_obj: Option, pub process_id: Option, + pub parameters: Option, } impl SqlQueryContext { @@ -166,6 +203,12 @@ impl SqlQueryContext { res.process_id = process_id; res } + + pub fn with_parameters(&self, parameters: &Option) -> Self { + let mut res = self.clone(); + res.parameters = parameters.clone(); + res + } } pub struct SqlServiceImpl { @@ -604,18 +647,20 @@ impl SqlService for SqlServiceImpl { #[instrument(level = "trace", skip(self))] async fn exec_query_with_context( &self, - context: SqlQueryContext, + mut context: SqlQueryContext, query: &str, ) -> Result, CubeError> { if !query.to_lowercase().starts_with("insert") && !query.to_lowercase().contains("password") { trace!("Query: '{}'", query); } + if let Some(data_frame) = SqlServiceImpl::handle_workbench_queries(query) { return Ok(Arc::new(data_frame)); } + let ast = { - let mut parser = CubeStoreParser::new(query)?; + let mut parser = CubeStoreParser::new(query, context.parameters.take())?; parser.parse_statement()? }; // trace!("AST is: {:?}", ast); @@ -1242,7 +1287,7 @@ impl SqlService for SqlServiceImpl { ) -> Result { let ast = { let replaced_quote = q.replace("\\'", "''"); - let mut parser = CubeStoreParser::new(&replaced_quote)?; + let mut parser = CubeStoreParser::new(&replaced_quote, context.parameters)?; parser.parse_statement()? }; match ast { diff --git a/rust/cubestore/cubestore/src/sql/parser.rs b/rust/cubestore/cubestore/src/sql/parser.rs index 9e2284d717080..56514af69bcfb 100644 --- a/rust/cubestore/cubestore/src/sql/parser.rs +++ b/rust/cubestore/cubestore/src/sql/parser.rs @@ -1,4 +1,5 @@ use crate::cachestore::{QueueItemStatus, QueueKey}; +use crate::sql::{QueryParameter, QueryParameters}; use sqlparser::ast::{ ColumnDef, CreateIndex, CreateTable, HiveDistributionStyle, Ident, ObjectName, Query, SqlOption, Statement as SQLStatement, Value, @@ -218,8 +219,12 @@ pub enum CacheStoreCommand { Persist, } +type QueryParameterHolder = Option; + pub struct CubeStoreParser<'a> { parser: Parser<'a>, + parameters: Option>, + placeholder_index: usize, } macro_rules! parse_sql_options { @@ -251,12 +256,16 @@ macro_rules! parse_sql_options { } impl<'a> CubeStoreParser<'a> { - pub fn new(sql: &str) -> Result { + pub fn new(sql: &str, parameters: Option) -> Result { let dialect = &MySqlDialectWithBackTicks {}; let mut tokenizer = Tokenizer::new(dialect, sql); let tokens = tokenizer.tokenize()?; + Ok(CubeStoreParser { parser: Parser::new(dialect).with_tokens(tokens), + parameters: parameters + .map(|parameters| parameters.into_iter().map(|p| Some(p)).collect()), + placeholder_index: 0, }) } @@ -300,6 +309,23 @@ impl<'a> CubeStoreParser<'a> { fn parse_queue_key(&mut self) -> Result { match self.parser.peek_token().token { + Token::Placeholder(placeholder) => { + self.parser.next_token(); + + match self.unwrap_placeholder(&placeholder)? { + QueryParameter::StringValue(v) => Ok(QueueKey::ByPath(v)), + QueryParameter::Int64Value(v) => { + let id = QueryParameter::Int64Value(v) + .try_as_u64() + .map_err(ParserError::ParserError)?; + Ok(QueueKey::ById(id)) + } + other => Err(ParserError::ParserError(format!( + "Wrong parameters type, actual: {}, expected: string or integer parameter", + other.get_type() + ))), + } + } Token::Word(w) => { self.parser.next_token(); @@ -345,6 +371,80 @@ impl<'a> CubeStoreParser<'a> { } } + fn unwrap_placeholder(&mut self, placeholder: &str) -> Result { + let parameters = if let Some(parameters) = self.parameters.as_mut() { + parameters + } else { + return Err(ParserError::ParserError( + "Empty parameters, please send parameters within query".to_string(), + )); + }; + + let placeholder_index = if placeholder.len() > 1 && placeholder[0..1] == *"$" { + return Err(ParserError::ParserError( + "Named placeholder are not supported, please use ?".to_string(), + )); + } else { + let n = self.placeholder_index; + + self.placeholder_index += 1; + + n + }; + + if parameters.len() <= placeholder_index { + return Err(ParserError::ParserError(format!( + "Placeholder index is out of bound, actual: {}, parameters length: {}", + placeholder_index, + parameters.len() + ))); + } + + if let Some(v) = parameters[placeholder_index].take() { + Ok(v) + } else { + return Err(ParserError::ParserError( + "Empty parameters, please send parameters within query".to_string(), + )); + } + } + + fn parse_literal_string(&mut self) -> Result { + if let Token::Placeholder(placeholder) = self.parser.peek_token().token { + self.parser.next_token(); + + match self.unwrap_placeholder(&placeholder)? { + QueryParameter::StringValue(s) => Ok(s), + other => Err(ParserError::ParserError(format!( + "Wrong parameters type, actual: {}, expected: string parameter", + other.get_type() + ))), + } + } else { + self.parser.parse_literal_string() + } + } + + fn parse_identifier(&mut self) -> Result { + if let Token::Placeholder(placeholder) = self.parser.peek_token().token { + self.parser.next_token(); + + match self.unwrap_placeholder(&placeholder)? { + QueryParameter::StringValue(value) => Ok(Ident { + value, + quote_style: None, + span: Span::empty(), + }), + other => Err(ParserError::ParserError(format!( + "Wrong parameters type, actual: {}, expected: string parameter", + other.get_type() + ))), + } + } else { + self.parser.parse_identifier() + } + } + fn parse_cache(&mut self) -> Result { let method = match self.parser.next_token().token { Token::Word(w) => w.value.to_ascii_lowercase(), @@ -366,23 +466,23 @@ impl<'a> CubeStoreParser<'a> { }; CacheCommand::Set { - key: self.parser.parse_identifier()?, - value: self.parser.parse_literal_string()?, + key: self.parse_identifier()?, + value: self.parse_literal_string()?, ttl, nx, } } "get" => CacheCommand::Get { - key: self.parser.parse_identifier()?, + key: self.parse_identifier()?, }, "keys" => CacheCommand::Keys { - prefix: self.parser.parse_identifier()?, + prefix: self.parse_identifier()?, }, "incr" => CacheCommand::Incr { - path: self.parser.parse_identifier()?, + path: self.parse_identifier()?, }, "remove" => CacheCommand::Remove { - key: self.parser.parse_identifier()?, + key: self.parse_identifier()?, }, "truncate" => CacheCommand::Truncate {}, other => { @@ -404,6 +504,25 @@ impl<'a> CubeStoreParser<'a> { where ::Err: std::fmt::Display, { + if let Token::Placeholder(placeholder) = self.parser.peek_token().token { + self.parser.next_token(); + + return match self.unwrap_placeholder(&placeholder)? { + QueryParameter::Int64Value(value) => { + value.to_string().parse::().map_err(|err| { + ParserError::ParserError(format!( + "{} must be a valid integer, error: {}", + var_name, err + )) + }) + } + other => Err(ParserError::ParserError(format!( + "Wrong parameters type, actual: {}, expected: int64 parameter", + other.get_type() + ))), + }; + } + let is_negative = match self.parser.peek_token().token { Token::Minus => { self.parser.next_token(); @@ -524,8 +643,8 @@ impl<'a> CubeStoreParser<'a> { exclusive, priority, orphaned, - key: self.parser.parse_identifier()?, - value: self.parser.parse_literal_string()?, + key: self.parse_identifier()?, + value: self.parse_literal_string()?, external_id, } } @@ -540,7 +659,7 @@ impl<'a> CubeStoreParser<'a> { let result = if self.parser.parse_keyword(Keyword::NULL) { None } else { - Some(self.parser.parse_literal_string()?) + Some(self.parse_literal_string()?) }; QueueCommand::Ack { key, result } @@ -910,7 +1029,7 @@ mod tests { use sqlparser::ast::Statement as SQLStatement; fn parse_stmt(query: &str) -> Result { - let mut parser = CubeStoreParser::new(query)?; + let mut parser = CubeStoreParser::new(query, None)?; Ok(parser.parse_statement()?) } @@ -1059,6 +1178,46 @@ mod tests { Ok(()) } + #[test] + fn parse_placeholder_index_out_of_bounds() -> Result<(), CubeError> { + // Two placeholders but only one parameter supplied — second placeholder + // must return a ParserError, not panic with index-out-of-bounds. + { + let mut parser = CubeStoreParser::new( + "QUEUE ACK ? ?", + Some(vec![QueryParameter::StringValue("a".to_string())]), + )?; + + let res = parser.parse_statement(); + assert!(res.is_err(), "expected parse error, got: {:?}", res); + + let msg = res.unwrap_err().to_string(); + assert!( + msg.contains("Placeholder index is out of bound"), + "unexpected error: {}", + msg + ); + } + + // Zero placeholders consumed but a placeholder is present in SQL with + // empty parameters list — must error, not panic. + { + let mut parser = CubeStoreParser::new("QUEUE ACK ?", Some(vec![]))?; + + let res = parser.parse_statement(); + assert!(res.is_err(), "expected parse error, got: {:?}", res); + + let msg = res.unwrap_err().to_string(); + assert!( + msg.contains("Placeholder index is out of bound"), + "unexpected error: {}", + msg + ); + } + + Ok(()) + } + #[test] fn parse_metastore_set_current() -> Result<(), CubeError> { let res = parse_stmt("sys MeTasTore SEt_Current 1671235558783")?; diff --git a/rust/cubestore/cubestore/src/sql/table_creator.rs b/rust/cubestore/cubestore/src/sql/table_creator.rs index 7ff3e13acbfbf..956021a3d9489 100644 --- a/rust/cubestore/cubestore/src/sql/table_creator.rs +++ b/rust/cubestore/cubestore/src/sql/table_creator.rs @@ -463,7 +463,7 @@ impl TableCreator { let trace_obj_to_save = trace_obj.clone(); let source_columns = if let Some(source_table) = source_table { - let mut parser = CubeStoreParser::new(&source_table)?; + let mut parser = CubeStoreParser::new(&source_table, None)?; let cols = parser .parse_streaming_source_table() .map_err(|e| CubeError::user(format!("Unexpected source_table param: {}", e)))?;