diff --git a/clients/client-relational/.eslintrc b/clients/client-relational/.eslintrc index 87ab180..4395479 100644 --- a/clients/client-relational/.eslintrc +++ b/clients/client-relational/.eslintrc @@ -4,6 +4,9 @@ "project": "tsconfig.json" }, "rules": { - "@typescript-eslint/no-explicity-any": "off" + "@typescript-eslint/no-explicity-any": "off", + "@typescript-eslint/no-unsafe-assignment":"off", + "@typescript-eslint/no-unsafe-member-access":"off", + "@typescript-eslint/no-unsafe-argument":"off" } } diff --git a/clients/client-relational/bin/gen-stubs.js b/clients/client-relational/bin/gen-stubs.js index e66d758..2bf8f75 100644 --- a/clients/client-relational/bin/gen-stubs.js +++ b/clients/client-relational/bin/gen-stubs.js @@ -35,6 +35,7 @@ const protoConfig = [ `--ts_proto_opt=Mgoogle/protobuf/struct.proto=@topcoder-framework/lib-common`, `--ts_proto_opt=Mgoogle/protobuf/timestamp.proto=@topcoder-framework/lib-common`, `--proto_path ${PROTO_DIR} ${PROTO_DIR}/data-access-layer/relational/*.proto`, + `--experimental_allow_proto3_optional`, ]; // https://github.com/stephenh/ts-proto#usage diff --git a/clients/client-relational/src/common/Op.ts b/clients/client-relational/src/common/Op.ts new file mode 100644 index 0000000..84db57e --- /dev/null +++ b/clients/client-relational/src/common/Op.ts @@ -0,0 +1,9 @@ +import { WhereGroupCondition, ConditionGroup } from "src/interfaces"; + +export function and(...conditions: WhereGroupCondition[]): ConditionGroup { + return ["and", conditions]; +} + +export function or(...conditions: WhereGroupCondition[]): ConditionGroup { + return ["or", conditions]; +} diff --git a/clients/client-relational/src/common/index.ts b/clients/client-relational/src/common/index.ts new file mode 100644 index 0000000..a9da38d --- /dev/null +++ b/clients/client-relational/src/common/index.ts @@ -0,0 +1 @@ +export * from "./Op"; diff --git a/clients/client-relational/src/index.ts b/clients/client-relational/src/index.ts index 5856907..b1d81ce 100644 --- a/clients/client-relational/src/index.ts +++ b/clients/client-relational/src/index.ts @@ -1,4 +1,5 @@ export * from "./client/RelationalClient"; +export * from "./common"; export * from "./interfaces/"; export * from "./models/data-access-layer/relational/relational"; export * from "./query/"; diff --git a/clients/client-relational/src/interfaces/Conditions.ts b/clients/client-relational/src/interfaces/Conditions.ts new file mode 100644 index 0000000..6850703 --- /dev/null +++ b/clients/client-relational/src/interfaces/Conditions.ts @@ -0,0 +1,18 @@ +import { + Operator, + TypedColumn, +} from "src/models/data-access-layer/relational/relational"; +import { ValueTypes } from "./ValueTypes"; + +export type ConditionGroup = [ + group: "and" | "or", + conditions: WhereGroupCondition[] +]; + +export type WhereCondition = [ + column: TypedColumn, + operator: Operator, + ...value: ValueTypes[] +]; + +export type WhereGroupCondition = WhereCondition | ConditionGroup; diff --git a/clients/client-relational/src/interfaces/Joiner.ts b/clients/client-relational/src/interfaces/Joiner.ts new file mode 100644 index 0000000..af9297d --- /dev/null +++ b/clients/client-relational/src/interfaces/Joiner.ts @@ -0,0 +1,8 @@ +import { + Join, + JoinCondition, +} from "src/models/data-access-layer/relational/relational"; + +export type Joiner = Omit, "type"> & { + conditions: Required[]; +}; diff --git a/clients/client-relational/src/interfaces/ModelAttributes.ts b/clients/client-relational/src/interfaces/ModelAttributes.ts new file mode 100644 index 0000000..be5e2ff --- /dev/null +++ b/clients/client-relational/src/interfaces/ModelAttributes.ts @@ -0,0 +1,5 @@ +import { TypedColumn } from "src/models/data-access-layer/relational/relational"; + +export type ModelAttributes = { + [Property in keyof T]-?: TypedColumn; +}; diff --git a/clients/client-relational/src/interfaces/ModelOptions.ts b/clients/client-relational/src/interfaces/ModelOptions.ts new file mode 100644 index 0000000..66cfb71 --- /dev/null +++ b/clients/client-relational/src/interfaces/ModelOptions.ts @@ -0,0 +1,18 @@ +type BaseModelOptions = { + schema: string; + table: string; +}; + +type ModelOptionsWithoutTimestamps = BaseModelOptions & { + timestamps?: false; +}; + +type ModelOptionsWithTimestamps = BaseModelOptions & { + timestamps: true; + createdAt: keyof T; + updatedAt: keyof T; +}; + +export type ModelOptions = + | ModelOptionsWithoutTimestamps + | ModelOptionsWithTimestamps; diff --git a/clients/client-relational/src/interfaces/Schema.ts b/clients/client-relational/src/interfaces/Schema.ts deleted file mode 100644 index 21ae32f..0000000 --- a/clients/client-relational/src/interfaces/Schema.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { TableColumns } from "./TableColumns"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type Schema> = { - dbSchema: string; - tableName: string; - idColumn?: string; - idSequence?: string; - idTable?: string; - columns: TableColumns; -}; diff --git a/clients/client-relational/src/interfaces/TableColumns.ts b/clients/client-relational/src/interfaces/TableColumns.ts index c4c5881..a9fb97c 100644 --- a/clients/client-relational/src/interfaces/TableColumns.ts +++ b/clients/client-relational/src/interfaces/TableColumns.ts @@ -1,10 +1,10 @@ -import { ColumnType } from "../models/data-access-layer/relational/relational"; +import { ColumnType } from "src/models/data-access-layer/relational/relational"; -export type TableColumn = { +type TableColumn = { name: string; type: ColumnType; }; -export type TableColumns> = { - [Property in keyof T]: TableColumn; +export type TableColumns = { + [Property in keyof T]-?: TableColumn; }; diff --git a/clients/client-relational/src/interfaces/ValueTypes.ts b/clients/client-relational/src/interfaces/ValueTypes.ts new file mode 100644 index 0000000..5ad3250 --- /dev/null +++ b/clients/client-relational/src/interfaces/ValueTypes.ts @@ -0,0 +1 @@ +export type ValueTypes = string | number | boolean | null; diff --git a/clients/client-relational/src/interfaces/index.ts b/clients/client-relational/src/interfaces/index.ts index ebd5d5d..9a38c96 100644 --- a/clients/client-relational/src/interfaces/index.ts +++ b/clients/client-relational/src/interfaces/index.ts @@ -1,2 +1,6 @@ -export * from "./Schema"; +export * from "./Conditions"; +export * from "./Joiner"; +export * from "./ModelAttributes"; +export * from "./ModelOptions"; export * from "./TableColumns"; +export * from "./ValueTypes"; diff --git a/clients/client-relational/src/models/data-access-layer/relational/relational.ts b/clients/client-relational/src/models/data-access-layer/relational/relational.ts index 68589eb..0826632 100644 --- a/clients/client-relational/src/models/data-access-layer/relational/relational.ts +++ b/clients/client-relational/src/models/data-access-layer/relational/relational.ts @@ -260,39 +260,80 @@ export interface Value { | { $case: "blobValue"; blobValue: Buffer }; } +export interface TypedColumn { + schema: string; + tableName: string; + name: string; + alias?: string | undefined; + type: ColumnType; +} + export interface Column { - tableName?: string | undefined; + schema: string; + tableName: string; name: string; - type?: ColumnType | undefined; } -export interface WhereCriteria { +export interface ReturningColumn { + name: string; + alias?: string | undefined; + type: ColumnType; +} + +export interface Condition { operator: Operator; - key: string; - value?: Value; + key?: Column; + value: Value[]; +} + +export interface AndWhere { + where: WhereCriteria[]; +} + +export interface OrWhere { + where: WhereCriteria[]; +} + +export interface WhereCriteria { + whereType?: + | { $case: "condition"; condition: Condition } + | { $case: "and"; and: AndWhere } + | { + $case: "or"; + or: OrWhere; + }; } export interface RawQuery { query: string; } +export interface Table { + schema: string; + tableName: string; +} + +export interface JoinCondition { + operator: Operator; + left?: Column; + right?: + | { $case: "value"; value: Value } + | { $case: "column"; column: Column }; +} + export interface Join { type: JoinType; - fromTableSchema?: string | undefined; - joinTableSchema?: string | undefined; - fromTable: string; - joinTable: string; - fromColumn: string; - joinColumn: string; + table?: Table; + conditions: JoinCondition[]; } export interface SelectQuery { - schema?: string | undefined; + schema: string; table: string; - column: Column[]; + column: TypedColumn[]; where: WhereCriteria[]; - groupBy: string[]; - orderBy: string[]; + groupBy: Column[]; + orderBy: Column[]; join: Join[]; limit: number; offset: number; @@ -304,12 +345,13 @@ export interface ColumnValue { } export interface InsertQuery { - schema?: string | undefined; + schema: string; table: string; columnValue: ColumnValue[]; idColumn?: string | undefined; idSequence?: string | undefined; idTable?: string | undefined; + returningFields: ReturningColumn[]; } export interface BulkInsertQuery { @@ -317,14 +359,14 @@ export interface BulkInsertQuery { } export interface UpdateQuery { - schema?: string | undefined; + schema: string; table: string; columnValue: ColumnValue[]; where: WhereCriteria[]; } export interface DeleteQuery { - schema?: string | undefined; + schema: string; table: string; where: WhereCriteria[]; } @@ -360,7 +402,13 @@ export interface RawQueryResult { } export interface InsertQueryResult { - lastInsertId: number; + insertResultType?: + | { $case: "lastInsertId"; lastInsertId: number } + | { $case: "row"; row: Row } + | { + $case: "affectedRows"; + affectedRows: number; + }; } export interface UpdateQueryResult { @@ -660,8 +708,124 @@ export const Value = { }, }; +function createBaseTypedColumn(): TypedColumn { + return { schema: "", tableName: "", name: "", alias: undefined, type: 0 }; +} + +export const TypedColumn = { + encode( + message: TypedColumn, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.schema !== "") { + writer.uint32(10).string(message.schema); + } + if (message.tableName !== "") { + writer.uint32(18).string(message.tableName); + } + if (message.name !== "") { + writer.uint32(26).string(message.name); + } + if (message.alias !== undefined) { + writer.uint32(34).string(message.alias); + } + if (message.type !== 0) { + writer.uint32(40).int32(message.type); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TypedColumn { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTypedColumn(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.schema = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.tableName = reader.string(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.name = reader.string(); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.alias = reader.string(); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.type = reader.int32() as any; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): TypedColumn { + return { + schema: isSet(object.schema) ? String(object.schema) : "", + tableName: isSet(object.tableName) ? String(object.tableName) : "", + name: isSet(object.name) ? String(object.name) : "", + alias: isSet(object.alias) ? String(object.alias) : undefined, + type: isSet(object.type) ? columnTypeFromJSON(object.type) : 0, + }; + }, + + toJSON(message: TypedColumn): unknown { + const obj: any = {}; + message.schema !== undefined && (obj.schema = message.schema); + message.tableName !== undefined && (obj.tableName = message.tableName); + message.name !== undefined && (obj.name = message.name); + message.alias !== undefined && (obj.alias = message.alias); + message.type !== undefined && (obj.type = columnTypeToJSON(message.type)); + return obj; + }, + + create, I>>(base?: I): TypedColumn { + return TypedColumn.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I + ): TypedColumn { + const message = createBaseTypedColumn(); + message.schema = object.schema ?? ""; + message.tableName = object.tableName ?? ""; + message.name = object.name ?? ""; + message.alias = object.alias ?? undefined; + message.type = object.type ?? 0; + return message; + }, +}; + function createBaseColumn(): Column { - return { tableName: undefined, name: "", type: undefined }; + return { schema: "", tableName: "", name: "" }; } export const Column = { @@ -669,14 +833,14 @@ export const Column = { message: Column, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.tableName !== undefined) { - writer.uint32(10).string(message.tableName); + if (message.schema !== "") { + writer.uint32(10).string(message.schema); } - if (message.name !== "") { - writer.uint32(18).string(message.name); + if (message.tableName !== "") { + writer.uint32(18).string(message.tableName); } - if (message.type !== undefined) { - writer.uint32(24).int32(message.type); + if (message.name !== "") { + writer.uint32(26).string(message.name); } return writer; }, @@ -694,21 +858,21 @@ export const Column = { break; } - message.tableName = reader.string(); + message.schema = reader.string(); continue; case 2: if (tag !== 18) { break; } - message.name = reader.string(); + message.tableName = reader.string(); continue; case 3: - if (tag !== 24) { + if (tag !== 26) { break; } - message.type = reader.int32() as any; + message.name = reader.string(); continue; } if ((tag & 7) === 4 || tag === 0) { @@ -721,21 +885,17 @@ export const Column = { fromJSON(object: any): Column { return { - tableName: isSet(object.tableName) ? String(object.tableName) : undefined, + schema: isSet(object.schema) ? String(object.schema) : "", + tableName: isSet(object.tableName) ? String(object.tableName) : "", name: isSet(object.name) ? String(object.name) : "", - type: isSet(object.type) ? columnTypeFromJSON(object.type) : undefined, }; }, toJSON(message: Column): unknown { const obj: any = {}; + message.schema !== undefined && (obj.schema = message.schema); message.tableName !== undefined && (obj.tableName = message.tableName); message.name !== undefined && (obj.name = message.name); - message.type !== undefined && - (obj.type = - message.type !== undefined - ? columnTypeToJSON(message.type) - : undefined); return obj; }, @@ -745,30 +905,373 @@ export const Column = { fromPartial, I>>(object: I): Column { const message = createBaseColumn(); - message.tableName = object.tableName ?? undefined; + message.schema = object.schema ?? ""; + message.tableName = object.tableName ?? ""; message.name = object.name ?? ""; - message.type = object.type ?? undefined; return message; }, }; -function createBaseWhereCriteria(): WhereCriteria { - return { operator: 0, key: "", value: undefined }; +function createBaseReturningColumn(): ReturningColumn { + return { name: "", alias: undefined, type: 0 }; } -export const WhereCriteria = { +export const ReturningColumn = { encode( - message: WhereCriteria, + message: ReturningColumn, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.name !== "") { + writer.uint32(10).string(message.name); + } + if (message.alias !== undefined) { + writer.uint32(18).string(message.alias); + } + if (message.type !== 0) { + writer.uint32(24).int32(message.type); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ReturningColumn { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseReturningColumn(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.name = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.alias = reader.string(); + continue; + case 3: + if (tag !== 24) { + break; + } + + message.type = reader.int32() as any; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): ReturningColumn { + return { + name: isSet(object.name) ? String(object.name) : "", + alias: isSet(object.alias) ? String(object.alias) : undefined, + type: isSet(object.type) ? columnTypeFromJSON(object.type) : 0, + }; + }, + + toJSON(message: ReturningColumn): unknown { + const obj: any = {}; + message.name !== undefined && (obj.name = message.name); + message.alias !== undefined && (obj.alias = message.alias); + message.type !== undefined && (obj.type = columnTypeToJSON(message.type)); + return obj; + }, + + create, I>>( + base?: I + ): ReturningColumn { + return ReturningColumn.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I + ): ReturningColumn { + const message = createBaseReturningColumn(); + message.name = object.name ?? ""; + message.alias = object.alias ?? undefined; + message.type = object.type ?? 0; + return message; + }, +}; + +function createBaseCondition(): Condition { + return { operator: 0, key: undefined, value: [] }; +} + +export const Condition = { + encode( + message: Condition, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { if (message.operator !== 0) { writer.uint32(8).int32(message.operator); } - if (message.key !== "") { - writer.uint32(18).string(message.key); + if (message.key !== undefined) { + Column.encode(message.key, writer.uint32(18).fork()).ldelim(); } - if (message.value !== undefined) { - Value.encode(message.value, writer.uint32(26).fork()).ldelim(); + for (const v of message.value) { + Value.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Condition { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseCondition(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.operator = reader.int32() as any; + continue; + case 2: + if (tag !== 18) { + break; + } + + message.key = Column.decode(reader, reader.uint32()); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.value.push(Value.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): Condition { + return { + operator: isSet(object.operator) ? operatorFromJSON(object.operator) : 0, + key: isSet(object.key) ? Column.fromJSON(object.key) : undefined, + value: Array.isArray(object?.value) + ? object.value.map((e: any) => Value.fromJSON(e)) + : [], + }; + }, + + toJSON(message: Condition): unknown { + const obj: any = {}; + message.operator !== undefined && + (obj.operator = operatorToJSON(message.operator)); + message.key !== undefined && + (obj.key = message.key ? Column.toJSON(message.key) : undefined); + if (message.value) { + obj.value = message.value.map((e) => (e ? Value.toJSON(e) : undefined)); + } else { + obj.value = []; + } + return obj; + }, + + create, I>>(base?: I): Condition { + return Condition.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I + ): Condition { + const message = createBaseCondition(); + message.operator = object.operator ?? 0; + message.key = + object.key !== undefined && object.key !== null + ? Column.fromPartial(object.key) + : undefined; + message.value = object.value?.map((e) => Value.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseAndWhere(): AndWhere { + return { where: [] }; +} + +export const AndWhere = { + encode( + message: AndWhere, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + for (const v of message.where) { + WhereCriteria.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AndWhere { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAndWhere(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.where.push(WhereCriteria.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): AndWhere { + return { + where: Array.isArray(object?.where) + ? object.where.map((e: any) => WhereCriteria.fromJSON(e)) + : [], + }; + }, + + toJSON(message: AndWhere): unknown { + const obj: any = {}; + if (message.where) { + obj.where = message.where.map((e) => + e ? WhereCriteria.toJSON(e) : undefined + ); + } else { + obj.where = []; + } + return obj; + }, + + create, I>>(base?: I): AndWhere { + return AndWhere.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): AndWhere { + const message = createBaseAndWhere(); + message.where = + object.where?.map((e) => WhereCriteria.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseOrWhere(): OrWhere { + return { where: [] }; +} + +export const OrWhere = { + encode( + message: OrWhere, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + for (const v of message.where) { + WhereCriteria.encode(v!, writer.uint32(10).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): OrWhere { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseOrWhere(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.where.push(WhereCriteria.decode(reader, reader.uint32())); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): OrWhere { + return { + where: Array.isArray(object?.where) + ? object.where.map((e: any) => WhereCriteria.fromJSON(e)) + : [], + }; + }, + + toJSON(message: OrWhere): unknown { + const obj: any = {}; + if (message.where) { + obj.where = message.where.map((e) => + e ? WhereCriteria.toJSON(e) : undefined + ); + } else { + obj.where = []; + } + return obj; + }, + + create, I>>(base?: I): OrWhere { + return OrWhere.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): OrWhere { + const message = createBaseOrWhere(); + message.where = + object.where?.map((e) => WhereCriteria.fromPartial(e)) || []; + return message; + }, +}; + +function createBaseWhereCriteria(): WhereCriteria { + return { whereType: undefined }; +} + +export const WhereCriteria = { + encode( + message: WhereCriteria, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + switch (message.whereType?.$case) { + case "condition": + Condition.encode( + message.whereType.condition, + writer.uint32(10).fork() + ).ldelim(); + break; + case "and": + AndWhere.encode( + message.whereType.and, + writer.uint32(18).fork() + ).ldelim(); + break; + case "or": + OrWhere.encode(message.whereType.or, writer.uint32(26).fork()).ldelim(); + break; } return writer; }, @@ -782,25 +1285,34 @@ export const WhereCriteria = { const tag = reader.uint32(); switch (tag >>> 3) { case 1: - if (tag !== 8) { + if (tag !== 10) { break; } - message.operator = reader.int32() as any; + message.whereType = { + $case: "condition", + condition: Condition.decode(reader, reader.uint32()), + }; continue; case 2: if (tag !== 18) { break; } - message.key = reader.string(); + message.whereType = { + $case: "and", + and: AndWhere.decode(reader, reader.uint32()), + }; continue; case 3: if (tag !== 26) { break; } - message.value = Value.decode(reader, reader.uint32()); + message.whereType = { + $case: "or", + or: OrWhere.decode(reader, reader.uint32()), + }; continue; } if ((tag & 7) === 4 || tag === 0) { @@ -813,19 +1325,33 @@ export const WhereCriteria = { fromJSON(object: any): WhereCriteria { return { - operator: isSet(object.operator) ? operatorFromJSON(object.operator) : 0, - key: isSet(object.key) ? String(object.key) : "", - value: isSet(object.value) ? Value.fromJSON(object.value) : undefined, + whereType: isSet(object.condition) + ? { + $case: "condition", + condition: Condition.fromJSON(object.condition), + } + : isSet(object.and) + ? { $case: "and", and: AndWhere.fromJSON(object.and) } + : isSet(object.or) + ? { $case: "or", or: OrWhere.fromJSON(object.or) } + : undefined, }; }, toJSON(message: WhereCriteria): unknown { const obj: any = {}; - message.operator !== undefined && - (obj.operator = operatorToJSON(message.operator)); - message.key !== undefined && (obj.key = message.key); - message.value !== undefined && - (obj.value = message.value ? Value.toJSON(message.value) : undefined); + message.whereType?.$case === "condition" && + (obj.condition = message.whereType?.condition + ? Condition.toJSON(message.whereType?.condition) + : undefined); + message.whereType?.$case === "and" && + (obj.and = message.whereType?.and + ? AndWhere.toJSON(message.whereType?.and) + : undefined); + message.whereType?.$case === "or" && + (obj.or = message.whereType?.or + ? OrWhere.toJSON(message.whereType?.or) + : undefined); return obj; }, @@ -839,12 +1365,36 @@ export const WhereCriteria = { object: I ): WhereCriteria { const message = createBaseWhereCriteria(); - message.operator = object.operator ?? 0; - message.key = object.key ?? ""; - message.value = - object.value !== undefined && object.value !== null - ? Value.fromPartial(object.value) - : undefined; + if ( + object.whereType?.$case === "condition" && + object.whereType?.condition !== undefined && + object.whereType?.condition !== null + ) { + message.whereType = { + $case: "condition", + condition: Condition.fromPartial(object.whereType.condition), + }; + } + if ( + object.whereType?.$case === "and" && + object.whereType?.and !== undefined && + object.whereType?.and !== null + ) { + message.whereType = { + $case: "and", + and: AndWhere.fromPartial(object.whereType.and), + }; + } + if ( + object.whereType?.$case === "or" && + object.whereType?.or !== undefined && + object.whereType?.or !== null + ) { + message.whereType = { + $case: "or", + or: OrWhere.fromPartial(object.whereType.or), + }; + } return message; }, }; @@ -909,49 +1459,109 @@ export const RawQuery = { }, }; -function createBaseJoin(): Join { - return { - type: 0, - fromTableSchema: undefined, - joinTableSchema: undefined, - fromTable: "", - joinTable: "", - fromColumn: "", - joinColumn: "", - }; +function createBaseTable(): Table { + return { schema: "", tableName: "" }; } -export const Join = { - encode(message: Join, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { - if (message.type !== 0) { - writer.uint32(8).int32(message.type); - } - if (message.fromTableSchema !== undefined) { - writer.uint32(18).string(message.fromTableSchema); +export const Table = { + encode(message: Table, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.schema !== "") { + writer.uint32(10).string(message.schema); } - if (message.joinTableSchema !== undefined) { - writer.uint32(26).string(message.joinTableSchema); + if (message.tableName !== "") { + writer.uint32(18).string(message.tableName); } - if (message.fromTable !== "") { - writer.uint32(34).string(message.fromTable); + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Table { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTable(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.schema = reader.string(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.tableName = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); } - if (message.joinTable !== "") { - writer.uint32(42).string(message.joinTable); + return message; + }, + + fromJSON(object: any): Table { + return { + schema: isSet(object.schema) ? String(object.schema) : "", + tableName: isSet(object.tableName) ? String(object.tableName) : "", + }; + }, + + toJSON(message: Table): unknown { + const obj: any = {}; + message.schema !== undefined && (obj.schema = message.schema); + message.tableName !== undefined && (obj.tableName = message.tableName); + return obj; + }, + + create, I>>(base?: I): Table { + return Table.fromPartial(base ?? {}); + }, + + fromPartial, I>>(object: I): Table { + const message = createBaseTable(); + message.schema = object.schema ?? ""; + message.tableName = object.tableName ?? ""; + return message; + }, +}; + +function createBaseJoinCondition(): JoinCondition { + return { operator: 0, left: undefined, right: undefined }; +} + +export const JoinCondition = { + encode( + message: JoinCondition, + writer: _m0.Writer = _m0.Writer.create() + ): _m0.Writer { + if (message.operator !== 0) { + writer.uint32(8).int32(message.operator); } - if (message.fromColumn !== "") { - writer.uint32(50).string(message.fromColumn); + if (message.left !== undefined) { + Column.encode(message.left, writer.uint32(18).fork()).ldelim(); } - if (message.joinColumn !== "") { - writer.uint32(58).string(message.joinColumn); + switch (message.right?.$case) { + case "value": + Value.encode(message.right.value, writer.uint32(26).fork()).ldelim(); + break; + case "column": + Column.encode(message.right.column, writer.uint32(34).fork()).ldelim(); + break; } return writer; }, - decode(input: _m0.Reader | Uint8Array, length?: number): Join { + decode(input: _m0.Reader | Uint8Array, length?: number): JoinCondition { const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); let end = length === undefined ? reader.len : reader.pos + length; - const message = createBaseJoin(); + const message = createBaseJoinCondition(); while (reader.pos < end) { const tag = reader.uint32(); switch (tag >>> 3) { @@ -960,49 +1570,160 @@ export const Join = { break; } - message.type = reader.int32() as any; + message.operator = reader.int32() as any; continue; case 2: if (tag !== 18) { break; } - message.fromTableSchema = reader.string(); + message.left = Column.decode(reader, reader.uint32()); continue; case 3: if (tag !== 26) { break; } - message.joinTableSchema = reader.string(); + message.right = { + $case: "value", + value: Value.decode(reader, reader.uint32()), + }; continue; case 4: if (tag !== 34) { break; } - message.fromTable = reader.string(); + message.right = { + $case: "column", + column: Column.decode(reader, reader.uint32()), + }; continue; - case 5: - if (tag !== 42) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + fromJSON(object: any): JoinCondition { + return { + operator: isSet(object.operator) ? operatorFromJSON(object.operator) : 0, + left: isSet(object.left) ? Column.fromJSON(object.left) : undefined, + right: isSet(object.value) + ? { $case: "value", value: Value.fromJSON(object.value) } + : isSet(object.column) + ? { $case: "column", column: Column.fromJSON(object.column) } + : undefined, + }; + }, + + toJSON(message: JoinCondition): unknown { + const obj: any = {}; + message.operator !== undefined && + (obj.operator = operatorToJSON(message.operator)); + message.left !== undefined && + (obj.left = message.left ? Column.toJSON(message.left) : undefined); + message.right?.$case === "value" && + (obj.value = message.right?.value + ? Value.toJSON(message.right?.value) + : undefined); + message.right?.$case === "column" && + (obj.column = message.right?.column + ? Column.toJSON(message.right?.column) + : undefined); + return obj; + }, + + create, I>>( + base?: I + ): JoinCondition { + return JoinCondition.fromPartial(base ?? {}); + }, + + fromPartial, I>>( + object: I + ): JoinCondition { + const message = createBaseJoinCondition(); + message.operator = object.operator ?? 0; + message.left = + object.left !== undefined && object.left !== null + ? Column.fromPartial(object.left) + : undefined; + if ( + object.right?.$case === "value" && + object.right?.value !== undefined && + object.right?.value !== null + ) { + message.right = { + $case: "value", + value: Value.fromPartial(object.right.value), + }; + } + if ( + object.right?.$case === "column" && + object.right?.column !== undefined && + object.right?.column !== null + ) { + message.right = { + $case: "column", + column: Column.fromPartial(object.right.column), + }; + } + return message; + }, +}; + +function createBaseJoin(): Join { + return { type: 0, table: undefined, conditions: [] }; +} + +export const Join = { + encode(message: Join, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== 0) { + writer.uint32(8).int32(message.type); + } + if (message.table !== undefined) { + Table.encode(message.table, writer.uint32(18).fork()).ldelim(); + } + for (const v of message.conditions) { + JoinCondition.encode(v!, writer.uint32(26).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): Join { + const reader = + input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseJoin(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { break; } - message.joinTable = reader.string(); + message.type = reader.int32() as any; continue; - case 6: - if (tag !== 50) { + case 2: + if (tag !== 18) { break; } - message.fromColumn = reader.string(); + message.table = Table.decode(reader, reader.uint32()); continue; - case 7: - if (tag !== 58) { + case 3: + if (tag !== 26) { break; } - message.joinColumn = reader.string(); + message.conditions.push( + JoinCondition.decode(reader, reader.uint32()) + ); continue; } if ((tag & 7) === 4 || tag === 0) { @@ -1016,30 +1737,25 @@ export const Join = { fromJSON(object: any): Join { return { type: isSet(object.type) ? joinTypeFromJSON(object.type) : 0, - fromTableSchema: isSet(object.fromTableSchema) - ? String(object.fromTableSchema) - : undefined, - joinTableSchema: isSet(object.joinTableSchema) - ? String(object.joinTableSchema) - : undefined, - fromTable: isSet(object.fromTable) ? String(object.fromTable) : "", - joinTable: isSet(object.joinTable) ? String(object.joinTable) : "", - fromColumn: isSet(object.fromColumn) ? String(object.fromColumn) : "", - joinColumn: isSet(object.joinColumn) ? String(object.joinColumn) : "", + table: isSet(object.table) ? Table.fromJSON(object.table) : undefined, + conditions: Array.isArray(object?.conditions) + ? object.conditions.map((e: any) => JoinCondition.fromJSON(e)) + : [], }; }, toJSON(message: Join): unknown { const obj: any = {}; message.type !== undefined && (obj.type = joinTypeToJSON(message.type)); - message.fromTableSchema !== undefined && - (obj.fromTableSchema = message.fromTableSchema); - message.joinTableSchema !== undefined && - (obj.joinTableSchema = message.joinTableSchema); - message.fromTable !== undefined && (obj.fromTable = message.fromTable); - message.joinTable !== undefined && (obj.joinTable = message.joinTable); - message.fromColumn !== undefined && (obj.fromColumn = message.fromColumn); - message.joinColumn !== undefined && (obj.joinColumn = message.joinColumn); + message.table !== undefined && + (obj.table = message.table ? Table.toJSON(message.table) : undefined); + if (message.conditions) { + obj.conditions = message.conditions.map((e) => + e ? JoinCondition.toJSON(e) : undefined + ); + } else { + obj.conditions = []; + } return obj; }, @@ -1050,19 +1766,19 @@ export const Join = { fromPartial, I>>(object: I): Join { const message = createBaseJoin(); message.type = object.type ?? 0; - message.fromTableSchema = object.fromTableSchema ?? undefined; - message.joinTableSchema = object.joinTableSchema ?? undefined; - message.fromTable = object.fromTable ?? ""; - message.joinTable = object.joinTable ?? ""; - message.fromColumn = object.fromColumn ?? ""; - message.joinColumn = object.joinColumn ?? ""; + message.table = + object.table !== undefined && object.table !== null + ? Table.fromPartial(object.table) + : undefined; + message.conditions = + object.conditions?.map((e) => JoinCondition.fromPartial(e)) || []; return message; }, }; function createBaseSelectQuery(): SelectQuery { return { - schema: undefined, + schema: "", table: "", column: [], where: [], @@ -1079,23 +1795,23 @@ export const SelectQuery = { message: SelectQuery, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.schema !== undefined) { + if (message.schema !== "") { writer.uint32(10).string(message.schema); } if (message.table !== "") { writer.uint32(18).string(message.table); } for (const v of message.column) { - Column.encode(v!, writer.uint32(26).fork()).ldelim(); + TypedColumn.encode(v!, writer.uint32(26).fork()).ldelim(); } for (const v of message.where) { WhereCriteria.encode(v!, writer.uint32(34).fork()).ldelim(); } for (const v of message.groupBy) { - writer.uint32(42).string(v!); + Column.encode(v!, writer.uint32(42).fork()).ldelim(); } for (const v of message.orderBy) { - writer.uint32(50).string(v!); + Column.encode(v!, writer.uint32(50).fork()).ldelim(); } for (const v of message.join) { Join.encode(v!, writer.uint32(58).fork()).ldelim(); @@ -1136,7 +1852,7 @@ export const SelectQuery = { break; } - message.column.push(Column.decode(reader, reader.uint32())); + message.column.push(TypedColumn.decode(reader, reader.uint32())); continue; case 4: if (tag !== 34) { @@ -1150,14 +1866,14 @@ export const SelectQuery = { break; } - message.groupBy.push(reader.string()); + message.groupBy.push(Column.decode(reader, reader.uint32())); continue; case 6: if (tag !== 50) { break; } - message.orderBy.push(reader.string()); + message.orderBy.push(Column.decode(reader, reader.uint32())); continue; case 7: if (tag !== 58) { @@ -1191,19 +1907,19 @@ export const SelectQuery = { fromJSON(object: any): SelectQuery { return { - schema: isSet(object.schema) ? String(object.schema) : undefined, + schema: isSet(object.schema) ? String(object.schema) : "", table: isSet(object.table) ? String(object.table) : "", column: Array.isArray(object?.column) - ? object.column.map((e: any) => Column.fromJSON(e)) + ? object.column.map((e: any) => TypedColumn.fromJSON(e)) : [], where: Array.isArray(object?.where) ? object.where.map((e: any) => WhereCriteria.fromJSON(e)) : [], groupBy: Array.isArray(object?.groupBy) - ? object.groupBy.map((e: any) => String(e)) + ? object.groupBy.map((e: any) => Column.fromJSON(e)) : [], orderBy: Array.isArray(object?.orderBy) - ? object.orderBy.map((e: any) => String(e)) + ? object.orderBy.map((e: any) => Column.fromJSON(e)) : [], join: Array.isArray(object?.join) ? object.join.map((e: any) => Join.fromJSON(e)) @@ -1219,7 +1935,7 @@ export const SelectQuery = { message.table !== undefined && (obj.table = message.table); if (message.column) { obj.column = message.column.map((e) => - e ? Column.toJSON(e) : undefined + e ? TypedColumn.toJSON(e) : undefined ); } else { obj.column = []; @@ -1232,12 +1948,16 @@ export const SelectQuery = { obj.where = []; } if (message.groupBy) { - obj.groupBy = message.groupBy.map((e) => e); + obj.groupBy = message.groupBy.map((e) => + e ? Column.toJSON(e) : undefined + ); } else { obj.groupBy = []; } if (message.orderBy) { - obj.orderBy = message.orderBy.map((e) => e); + obj.orderBy = message.orderBy.map((e) => + e ? Column.toJSON(e) : undefined + ); } else { obj.orderBy = []; } @@ -1259,13 +1979,14 @@ export const SelectQuery = { object: I ): SelectQuery { const message = createBaseSelectQuery(); - message.schema = object.schema ?? undefined; + message.schema = object.schema ?? ""; message.table = object.table ?? ""; - message.column = object.column?.map((e) => Column.fromPartial(e)) || []; + message.column = + object.column?.map((e) => TypedColumn.fromPartial(e)) || []; message.where = object.where?.map((e) => WhereCriteria.fromPartial(e)) || []; - message.groupBy = object.groupBy?.map((e) => e) || []; - message.orderBy = object.orderBy?.map((e) => e) || []; + message.groupBy = object.groupBy?.map((e) => Column.fromPartial(e)) || []; + message.orderBy = object.orderBy?.map((e) => Column.fromPartial(e)) || []; message.join = object.join?.map((e) => Join.fromPartial(e)) || []; message.limit = object.limit ?? 0; message.offset = object.offset ?? 0; @@ -1356,12 +2077,13 @@ export const ColumnValue = { function createBaseInsertQuery(): InsertQuery { return { - schema: undefined, + schema: "", table: "", columnValue: [], idColumn: undefined, idSequence: undefined, idTable: undefined, + returningFields: [], }; } @@ -1370,7 +2092,7 @@ export const InsertQuery = { message: InsertQuery, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.schema !== undefined) { + if (message.schema !== "") { writer.uint32(10).string(message.schema); } if (message.table !== "") { @@ -1388,6 +2110,9 @@ export const InsertQuery = { if (message.idTable !== undefined) { writer.uint32(50).string(message.idTable); } + for (const v of message.returningFields) { + ReturningColumn.encode(v!, writer.uint32(58).fork()).ldelim(); + } return writer; }, @@ -1441,6 +2166,15 @@ export const InsertQuery = { message.idTable = reader.string(); continue; + case 7: + if (tag !== 58) { + break; + } + + message.returningFields.push( + ReturningColumn.decode(reader, reader.uint32()) + ); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -1452,7 +2186,7 @@ export const InsertQuery = { fromJSON(object: any): InsertQuery { return { - schema: isSet(object.schema) ? String(object.schema) : undefined, + schema: isSet(object.schema) ? String(object.schema) : "", table: isSet(object.table) ? String(object.table) : "", columnValue: Array.isArray(object?.columnValue) ? object.columnValue.map((e: any) => ColumnValue.fromJSON(e)) @@ -1462,6 +2196,9 @@ export const InsertQuery = { ? String(object.idSequence) : undefined, idTable: isSet(object.idTable) ? String(object.idTable) : undefined, + returningFields: Array.isArray(object?.returningFields) + ? object.returningFields.map((e: any) => ReturningColumn.fromJSON(e)) + : [], }; }, @@ -1479,6 +2216,13 @@ export const InsertQuery = { message.idColumn !== undefined && (obj.idColumn = message.idColumn); message.idSequence !== undefined && (obj.idSequence = message.idSequence); message.idTable !== undefined && (obj.idTable = message.idTable); + if (message.returningFields) { + obj.returningFields = message.returningFields.map((e) => + e ? ReturningColumn.toJSON(e) : undefined + ); + } else { + obj.returningFields = []; + } return obj; }, @@ -1490,13 +2234,15 @@ export const InsertQuery = { object: I ): InsertQuery { const message = createBaseInsertQuery(); - message.schema = object.schema ?? undefined; + message.schema = object.schema ?? ""; message.table = object.table ?? ""; message.columnValue = object.columnValue?.map((e) => ColumnValue.fromPartial(e)) || []; message.idColumn = object.idColumn ?? undefined; message.idSequence = object.idSequence ?? undefined; message.idTable = object.idTable ?? undefined; + message.returningFields = + object.returningFields?.map((e) => ReturningColumn.fromPartial(e)) || []; return message; }, }; @@ -1577,7 +2323,7 @@ export const BulkInsertQuery = { }; function createBaseUpdateQuery(): UpdateQuery { - return { schema: undefined, table: "", columnValue: [], where: [] }; + return { schema: "", table: "", columnValue: [], where: [] }; } export const UpdateQuery = { @@ -1585,7 +2331,7 @@ export const UpdateQuery = { message: UpdateQuery, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.schema !== undefined) { + if (message.schema !== "") { writer.uint32(10).string(message.schema); } if (message.table !== "") { @@ -1647,7 +2393,7 @@ export const UpdateQuery = { fromJSON(object: any): UpdateQuery { return { - schema: isSet(object.schema) ? String(object.schema) : undefined, + schema: isSet(object.schema) ? String(object.schema) : "", table: isSet(object.table) ? String(object.table) : "", columnValue: Array.isArray(object?.columnValue) ? object.columnValue.map((e: any) => ColumnValue.fromJSON(e)) @@ -1687,7 +2433,7 @@ export const UpdateQuery = { object: I ): UpdateQuery { const message = createBaseUpdateQuery(); - message.schema = object.schema ?? undefined; + message.schema = object.schema ?? ""; message.table = object.table ?? ""; message.columnValue = object.columnValue?.map((e) => ColumnValue.fromPartial(e)) || []; @@ -1698,7 +2444,7 @@ export const UpdateQuery = { }; function createBaseDeleteQuery(): DeleteQuery { - return { schema: undefined, table: "", where: [] }; + return { schema: "", table: "", where: [] }; } export const DeleteQuery = { @@ -1706,7 +2452,7 @@ export const DeleteQuery = { message: DeleteQuery, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.schema !== undefined) { + if (message.schema !== "") { writer.uint32(10).string(message.schema); } if (message.table !== "") { @@ -1758,7 +2504,7 @@ export const DeleteQuery = { fromJSON(object: any): DeleteQuery { return { - schema: isSet(object.schema) ? String(object.schema) : undefined, + schema: isSet(object.schema) ? String(object.schema) : "", table: isSet(object.table) ? String(object.table) : "", where: Array.isArray(object?.where) ? object.where.map((e: any) => WhereCriteria.fromJSON(e)) @@ -1788,7 +2534,7 @@ export const DeleteQuery = { object: I ): DeleteQuery { const message = createBaseDeleteQuery(); - message.schema = object.schema ?? undefined; + message.schema = object.schema ?? ""; message.table = object.table ?? ""; message.where = object.where?.map((e) => WhereCriteria.fromPartial(e)) || []; @@ -2385,7 +3131,7 @@ export const RawQueryResult = { }; function createBaseInsertQueryResult(): InsertQueryResult { - return { lastInsertId: 0 }; + return { insertResultType: undefined }; } export const InsertQueryResult = { @@ -2393,8 +3139,19 @@ export const InsertQueryResult = { message: InsertQueryResult, writer: _m0.Writer = _m0.Writer.create() ): _m0.Writer { - if (message.lastInsertId !== 0) { - writer.uint32(8).uint64(message.lastInsertId); + switch (message.insertResultType?.$case) { + case "lastInsertId": + writer.uint32(8).uint64(message.insertResultType.lastInsertId); + break; + case "row": + Row.encode( + message.insertResultType.row, + writer.uint32(18).fork() + ).ldelim(); + break; + case "affectedRows": + writer.uint32(24).uint64(message.insertResultType.affectedRows); + break; } return writer; }, @@ -2412,7 +3169,30 @@ export const InsertQueryResult = { break; } - message.lastInsertId = longToNumber(reader.uint64() as Long); + message.insertResultType = { + $case: "lastInsertId", + lastInsertId: longToNumber(reader.uint64() as Long), + }; + continue; + case 2: + if (tag !== 18) { + break; + } + + message.insertResultType = { + $case: "row", + row: Row.decode(reader, reader.uint32()), + }; + continue; + case 3: + if (tag !== 24) { + break; + } + + message.insertResultType = { + $case: "affectedRows", + affectedRows: longToNumber(reader.uint64() as Long), + }; continue; } if ((tag & 7) === 4 || tag === 0) { @@ -2425,16 +3205,26 @@ export const InsertQueryResult = { fromJSON(object: any): InsertQueryResult { return { - lastInsertId: isSet(object.lastInsertId) - ? Number(object.lastInsertId) - : 0, + insertResultType: isSet(object.lastInsertId) + ? { $case: "lastInsertId", lastInsertId: Number(object.lastInsertId) } + : isSet(object.row) + ? { $case: "row", row: Row.fromJSON(object.row) } + : isSet(object.affectedRows) + ? { $case: "affectedRows", affectedRows: Number(object.affectedRows) } + : undefined, }; }, toJSON(message: InsertQueryResult): unknown { const obj: any = {}; - message.lastInsertId !== undefined && - (obj.lastInsertId = Math.round(message.lastInsertId)); + message.insertResultType?.$case === "lastInsertId" && + (obj.lastInsertId = Math.round(message.insertResultType?.lastInsertId)); + message.insertResultType?.$case === "row" && + (obj.row = message.insertResultType?.row + ? Row.toJSON(message.insertResultType?.row) + : undefined); + message.insertResultType?.$case === "affectedRows" && + (obj.affectedRows = Math.round(message.insertResultType?.affectedRows)); return obj; }, @@ -2448,7 +3238,36 @@ export const InsertQueryResult = { object: I ): InsertQueryResult { const message = createBaseInsertQueryResult(); - message.lastInsertId = object.lastInsertId ?? 0; + if ( + object.insertResultType?.$case === "lastInsertId" && + object.insertResultType?.lastInsertId !== undefined && + object.insertResultType?.lastInsertId !== null + ) { + message.insertResultType = { + $case: "lastInsertId", + lastInsertId: object.insertResultType.lastInsertId, + }; + } + if ( + object.insertResultType?.$case === "row" && + object.insertResultType?.row !== undefined && + object.insertResultType?.row !== null + ) { + message.insertResultType = { + $case: "row", + row: Row.fromPartial(object.insertResultType.row), + }; + } + if ( + object.insertResultType?.$case === "affectedRows" && + object.insertResultType?.affectedRows !== undefined && + object.insertResultType?.affectedRows !== null + ) { + message.insertResultType = { + $case: "affectedRows", + affectedRows: object.insertResultType.affectedRows, + }; + } return message; }, }; diff --git a/clients/client-relational/src/query/BaseQuery.ts b/clients/client-relational/src/query/BaseQuery.ts deleted file mode 100644 index 00b50f3..0000000 --- a/clients/client-relational/src/query/BaseQuery.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Schema } from "../interfaces/Schema"; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export abstract class BaseQuery> { - protected schema: Schema; - - constructor(schema: Schema) { - this.schema = schema; - } -} diff --git a/clients/client-relational/src/query/Model.ts b/clients/client-relational/src/query/Model.ts new file mode 100644 index 0000000..eb6a94a --- /dev/null +++ b/clients/client-relational/src/query/Model.ts @@ -0,0 +1,569 @@ +import _ from "lodash"; +import { + ColumnType, + JoinType, + Query, + TypedColumn, + Value as RelationalValue, + WhereCriteria, + SelectQuery, + InsertQuery, + UpdateQuery, + DeleteQuery, +} from "../models/data-access-layer/relational/relational"; +import { + ModelAttributes, + TableColumns, + ModelOptions, + WhereGroupCondition, + WhereCondition, + ConditionGroup, + ValueTypes, + Joiner, +} from "src/interfaces"; + +interface Build { + build: () => Query; +} + +interface Select extends Build { + join: (join: Joiner) => Select; + leftJoin: (join: Joiner) => Select; + rightJoin: (join: Joiner) => Select; + fullJoin: (join: Joiner) => Select; + where: (...inputs: WhereCondition) => Where; + whereGroup: (...inputs: WhereGroupCondition[]) => Where; + orderBy: (...order: TypedColumn[]) => OrderBy; + limit: (limit: number) => Limit; + offset: (offset: number) => Build; +} + +interface Insert extends Build { + return: (...returning: TypedColumn[]) => Build; + build: () => Query; +} + +interface Update extends Build { + where: (...inputs: WhereCondition) => WhereForModify; + whereGroup: (...inputs: WhereGroupCondition[]) => WhereForModify; +} + +interface Delete { + where: (...inputs: WhereCondition) => WhereForModify; + whereGroup: (...inputs: WhereGroupCondition[]) => WhereForModify; +} + +interface OrderBy extends Build { + limit: (limit: number) => Limit; + offset: (offset: number) => Build; + build: () => Query; +} + +interface Limit extends Build { + offset: (offset: number) => Build; + build: () => Query; +} + +interface Where extends Build { + andWhere: (...inputs: WhereCondition) => Where; + andWhereGroup: (...inputs: WhereGroupCondition[]) => Where; + orderBy: (...order: TypedColumn[]) => OrderBy; + limit: (limit: number) => Limit; + offset: (offset: number) => Build; +} + +interface WhereForModify extends Build { + andWhere: (...inputs: WhereCondition) => WhereForModify; + andWhereGroup: (...inputs: WhereGroupCondition[]) => WhereForModify; +} + +type PartialOrNull = { + [P in keyof T]?: T[P] | null; +}; + +export class Model< + U = never, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends U extends Record ? any : any = U +> { + private schema: string; + private table: string; + private timestamps: boolean; + private createdAt?: TypedColumn; + private updatedAt?: TypedColumn; + public attributes: ModelAttributes; + + constructor(attributes: TableColumns, options: ModelOptions) { + this.schema = options.schema; + this.table = options.table; + this.timestamps = options.timestamps ?? false; + this.attributes = _.mapValues(attributes, (col, key) => { + return { + schema: this.schema, + tableName: this.table, + name: col.name, + alias: key, + type: col.type, + }; + }); + if (options.timestamps) { + this.createdAt = this.attributes[options.createdAt]; + this.updatedAt = this.attributes[options.updatedAt]; + } + } + + public select(...columns: TypedColumn[]): Select { + const query = { + schema: this.schema, + table: this.table, + column: columns, + where: [], + join: [], + groupBy: [], + orderBy: [], + limit: 100, + offset: 0, + }; + + return { + join: this.join.bind(this, query, JoinType.JOIN_TYPE_INNER), + leftJoin: this.join.bind(this, query, JoinType.JOIN_TYPE_LEFT), + rightJoin: this.join.bind(this, query, JoinType.JOIN_TYPE_RIGHT), + fullJoin: this.join.bind(this, query, JoinType.JOIN_TYPE_FULL), + where: this.where.bind(this, query), + whereGroup: this.whereGroup.bind(this, query), + orderBy: this.orderBy.bind(this, query), + limit: this.limit.bind(this, query), + offset: this.offset.bind(this, query), + build: this.build.bind(this, query, "select"), + }; + } + + public insert(input: Partial): Insert { + const query = { + schema: this.schema, + table: this.table, + columnValue: _.map( + _.filter( + _.entries(input), + ([key, value]) => + value !== undefined && + key !== this.createdAt?.alias && + key !== this.updatedAt?.alias + ), + ([key, value]) => ({ + column: this.attributes[key].name, + value: this.toRelationalValueForUpdate( + this.attributes[key].type, + value as T[string] + ), + }) + ), + returningFields: [], + }; + + if (this.timestamps && this.createdAt && this.updatedAt) { + query.columnValue.push( + { + column: this.createdAt.name, + value: { + value: { + $case: "datetimeValue", + // $NOW will be converted to relevant expression in data access later + datetimeValue: "$NOW", + }, + }, + }, + { + column: this.updatedAt.name, + value: { + value: { + $case: "datetimeValue", + datetimeValue: "$NOW", + }, + }, + } + ); + } + + return { + return: this.returning.bind(this, query), + build: this.build.bind(this, query, "insert"), + }; + } + + public update(input: PartialOrNull): Update { + const query = { + schema: this.schema, + table: this.table, + columnValue: _.map( + _.filter( + _.entries(input), + ([key, value]) => + value !== undefined && + key !== this.createdAt?.alias && + key !== this.updatedAt?.alias + ), + ([key, value]) => ({ + column: this.attributes[key].name, + value: this.toRelationalValueForUpdate( + this.attributes[key].type, + value as T[string] + ), + }) + ), + where: [], + }; + + if (this.timestamps && this.updatedAt) { + query.columnValue.push({ + column: this.updatedAt.name, + value: { + value: { + $case: "datetimeValue", + datetimeValue: "$NOW", + }, + }, + }); + } + + return { + where: this.whereForModify.bind(this, query, "update"), + whereGroup: this.whereGroupForModify.bind(this, query, "update"), + build: this.build.bind(this, query, "update"), + }; + } + + public delete(): Delete { + const query = { + schema: this.schema, + table: this.table, + where: [], + }; + + return { + where: this.whereForModify.bind(this, query, "delete"), + whereGroup: this.whereGroupForModify.bind(this, query, "delete"), + }; + } + + private join(query: SelectQuery, type: JoinType, join: Joiner): Select { + query.join.push(_.assign({}, join, { type })); + return { + join: this.join.bind(this, query, JoinType.JOIN_TYPE_INNER), + leftJoin: this.join.bind(this, query, JoinType.JOIN_TYPE_LEFT), + rightJoin: this.join.bind(this, query, JoinType.JOIN_TYPE_RIGHT), + fullJoin: this.join.bind(this, query, JoinType.JOIN_TYPE_FULL), + where: this.where.bind(this, query), + whereGroup: this.whereGroup.bind(this, query), + orderBy: this.orderBy.bind(this, query), + limit: this.limit.bind(this, query), + offset: this.offset.bind(this, query), + build: this.build.bind(this, query, "select"), + }; + } + + private where(query: SelectQuery, ...inputs: WhereCondition): Where { + if (!inputs.length) { + throw new Error("Where requires at least one argument"); + } + + query.where.push(this.buildForCondition(inputs)); + + return { + andWhere: this.where.bind(this, query), + andWhereGroup: this.whereGroup.bind(this, query), + orderBy: this.orderBy.bind(this, query), + limit: this.limit.bind(this, query), + offset: this.offset.bind(this, query), + build: this.build.bind(this, query, "select"), + }; + } + + private whereGroup( + query: SelectQuery, + ...inputs: WhereGroupCondition[] + ): Where { + if (!inputs.length) { + throw new Error("Where requires at least one argument"); + } + + for (const input of inputs) { + if (this.isConditionGroup(input)) { + query.where.push(this.buildForConditionGroup(input)); + } else { + query.where.push(this.buildForCondition(input)); + } + } + + return { + andWhere: this.where.bind(this, query), + andWhereGroup: this.whereGroup.bind(this, query), + orderBy: this.orderBy.bind(this, query), + limit: this.limit.bind(this, query), + offset: this.offset.bind(this, query), + build: this.build.bind(this, query, "select"), + }; + } + + private whereForModify( + query: UpdateQuery | DeleteQuery, + qType: "update" | "delete", + ...inputs: WhereCondition + ): WhereForModify { + if (!inputs.length) { + throw new Error("Where requires at least one argument"); + } + + query.where.push(this.buildForCondition(inputs)); + return { + andWhere: this.whereForModify.bind(this, query, qType), + andWhereGroup: this.whereGroupForModify.bind(this, query, qType), + build: this.build.bind(this, query, qType), + }; + } + + private whereGroupForModify( + query: UpdateQuery | DeleteQuery, + qType: "update" | "delete", + ...inputs: WhereGroupCondition[] + ): WhereForModify { + if (!inputs.length) { + throw new Error("Where requires at least one argument"); + } + + for (const input of inputs) { + if (this.isConditionGroup(input)) { + query.where.push(this.buildForConditionGroup(input)); + } else { + query.where.push(this.buildForCondition(input)); + } + } + + return { + andWhere: this.whereForModify.bind(this, query, qType), + andWhereGroup: this.whereGroupForModify.bind(this, query, qType), + build: this.build.bind(this, query, qType), + }; + } + + private buildForConditionGroup( + conditionGroup: ConditionGroup + ): Partial { + const [group, conditions] = conditionGroup; + + const wheres: WhereCriteria[] = _.map(conditions, (condition) => { + if (this.isConditionGroup(condition)) { + return this.buildForConditionGroup(condition); + } else { + return this.buildForCondition(condition); + } + }); + + const whereType: Partial = {}; + if (group === "and") { + whereType.whereType = { $case: "and", and: { where: wheres } }; + } else { + whereType.whereType = { $case: "or", or: { where: wheres } }; + } + + return whereType; + } + + private buildForCondition(condition: WhereCondition): Partial { + const [column, operator, ...value] = condition; + + return { + whereType: { + $case: "condition", + condition: { + key: column, + operator, + value: this.toRelationalValue(column.type, value), + }, + }, + }; + } + + private orderBy(query: SelectQuery, ...order: TypedColumn[]): OrderBy { + query.orderBy = _.map(order, (o) => + _.pick(o, ["schema", "tableName", "name"]) + ); + + return { + limit: this.limit.bind(this, query), + offset: this.offset.bind(this, query), + build: this.build.bind(this, query, "select"), + }; + } + + private limit(query: SelectQuery, limit: number): Limit { + query.limit = limit; + + return { + offset: this.offset.bind(this, query), + build: this.build.bind(this, query, "select"), + }; + } + + private offset(query: SelectQuery, offset: number): Build { + query.offset = offset; + + return { + build: this.build.bind(this, query, "select"), + }; + } + + private returning( + query: InsertQuery, + ...returningFields: TypedColumn[] + ): Build { + query.returningFields = _.map(returningFields, (rf) => + _.pick(rf, ["name", "type", "alias"]) + ); + return { + build: this.build.bind(this, query, "insert"), + }; + } + + private build( + query: SelectQuery | InsertQuery | UpdateQuery | DeleteQuery, + qType: "select" | "insert" | "update" | "delete" + ): Query { + if (qType === "select") { + return { query: { $case: qType, [qType]: query as SelectQuery } }; + } else if (qType === "insert") { + return { query: { $case: qType, [qType]: query as InsertQuery } }; + } else if (qType === "update") { + return { query: { $case: qType, [qType]: query as UpdateQuery } }; + } else if (qType === "delete") { + return { query: { $case: qType, [qType]: query as DeleteQuery } }; + } + throw new Error("This shouldn't happen"); + } + + private isConditionGroup(criteria: unknown): criteria is ConditionGroup { + return ( + _.isArray(criteria) && + criteria.length === 2 && + criteria[0] in ["and", "or"] && + _.isArray(criteria[2]) + ); + } + + private toRelationalValueForUpdate( + columnType: ColumnType, + value: ValueTypes + ): RelationalValue | undefined { + // is null or set null + if (value === null) { + return undefined; + } + + if (columnType === ColumnType.COLUMN_TYPE_INT) { + return { value: { $case: "intValue", intValue: value as number } }; + } + + if (columnType === ColumnType.COLUMN_TYPE_LONG) { + return { value: { $case: "longValue", longValue: value as number } }; + } + + if (columnType === ColumnType.COLUMN_TYPE_FLOAT) { + return { value: { $case: "floatValue", floatValue: value as number } }; + } + + if (columnType === ColumnType.COLUMN_TYPE_DOUBLE) { + return { value: { $case: "doubleValue", doubleValue: value as number } }; + } + + if (columnType === ColumnType.COLUMN_TYPE_DATE) { + return { value: { $case: "dateValue", dateValue: value as string } }; + } + + if (columnType == ColumnType.COLUMN_TYPE_DATETIME) { + return { + value: { $case: "datetimeValue", datetimeValue: value as string }, + }; + } + + if (columnType == ColumnType.COLUMN_TYPE_STRING) { + return { value: { $case: "stringValue", stringValue: value as string } }; + } + + if (columnType == ColumnType.COLUMN_TYPE_BOOLEAN) { + return { + value: { $case: "booleanValue", booleanValue: value as boolean }, + }; + } + + throw new Error(`Unsupported data type ${columnType}`); + } + + private toRelationalValue( + columnType: ColumnType, + values: ValueTypes[] + ): RelationalValue[] { + // is null or set null + if (values.length === 0) { + throw new Error(`Value array should not be empty`); + } + + if (columnType === ColumnType.COLUMN_TYPE_INT) { + return _.map(values, (value) => { + return { value: { $case: "intValue", intValue: value as number } }; + }); + } + + if (columnType === ColumnType.COLUMN_TYPE_LONG) { + return _.map(values, (value) => { + return { value: { $case: "longValue", longValue: value as number } }; + }); + } + + if (columnType === ColumnType.COLUMN_TYPE_FLOAT) { + return _.map(values, (value) => { + return { value: { $case: "floatValue", floatValue: value as number } }; + }); + } + + if (columnType === ColumnType.COLUMN_TYPE_DOUBLE) { + return _.map(values, (value) => { + return { + value: { $case: "doubleValue", doubleValue: value as number }, + }; + }); + } + + if (columnType === ColumnType.COLUMN_TYPE_DATE) { + return _.map(values, (value) => { + return { value: { $case: "dateValue", dateValue: value as string } }; + }); + } + + if (columnType == ColumnType.COLUMN_TYPE_DATETIME) { + return _.map(values, (value) => { + return { + value: { $case: "datetimeValue", datetimeValue: value as string }, + }; + }); + } + + if (columnType == ColumnType.COLUMN_TYPE_STRING) { + return _.map(values, (value) => { + return { + value: { $case: "stringValue", stringValue: value as string }, + }; + }); + } + + if (columnType == ColumnType.COLUMN_TYPE_BOOLEAN) { + return _.map(values, (value) => { + return { + value: { $case: "booleanValue", booleanValue: value as boolean }, + }; + }); + } + + throw new Error(`Unsupported data type ${columnType}`); + } +} diff --git a/clients/client-relational/src/query/QueryBuilder.ts b/clients/client-relational/src/query/QueryBuilder.ts deleted file mode 100644 index 7cf2bc4..0000000 --- a/clients/client-relational/src/query/QueryBuilder.ts +++ /dev/null @@ -1,472 +0,0 @@ -import { - ScanCriteria, - Value, - Operator as LibCommonOperator, -} from "@topcoder-framework/lib-common"; -import { TableColumn } from "../interfaces/TableColumns"; -import { - ColumnType, - Operator, - Query, - Value as RelationalValue, -} from "../models/data-access-layer/relational/relational"; - -import { BaseQuery } from "./BaseQuery"; - -export type WhereCondition = - | [column: TableColumn, operator: Operator, value: RelationalValue] - | ScanCriteria[]; - -interface Build { - build: () => Query; -} - -interface Limit extends Build { - offset: (offset: number) => Build; - build: () => Query; -} - -interface Where extends Build { - andWhere: (...inputs: WhereCondition) => Where; - limit: (limit: number) => Limit; - offset: (offset: number) => Build; -} - -interface WhereForModify extends Build { - andWhere: (...inputs: WhereCondition) => WhereForModify; -} - -interface Select extends Build { - where: (...inputs: WhereCondition) => Where; - limit: (limit: number) => Limit; - offset: (offset: number) => Build; -} - -interface Update extends Build { - where: (...inputs: WhereCondition) => WhereForModify; -} - -interface Delete { - where: (...inputs: WhereCondition) => WhereForModify; -} - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export class QueryBuilder> extends BaseQuery { - #query: Query | null = null; - - public select(...columns: TableColumn[]): Select { - this.#query = { - query: { - $case: "select", - select: { - schema: this.schema.dbSchema, - table: this.schema.tableName, - column: columns.map((col) => ({ - tableName: this.schema.tableName, - name: col.name, - type: col.type, - })), - where: [], - join: [], - groupBy: [], - orderBy: [], - limit: 100, - offset: 0, - }, - }, - }; - - return { - where: this.where.bind(this), - // orderBy: this.orderBy.bind(this), - limit: this.limit.bind(this), - offset: this.offset.bind(this), - build: this.build.bind(this), - }; - } - - public insert>( - input: CreateInput - ): Build { - // TODO: This is a hack to get the create and modify audit columns, we need to either use the same "column name" or add a new property to the schema - let createAuditColumnName = "create_date"; - let updateAuditColumnName = "modify_date"; - - let tableHasCreateAudit = false; - - if (this.schema.columns.createDate != null) { - tableHasCreateAudit = true; - createAuditColumnName = this.schema.columns.createDate.name; - } else if (this.schema.columns.createTime != null) { - tableHasCreateAudit = true; - createAuditColumnName = this.schema.columns.createTime.name; - } - - if (this.schema.columns.modifyDate != null) { - updateAuditColumnName = this.schema.columns.modifyDate.name; - } else if (this.schema.columns.modifyTime != null) { - updateAuditColumnName = this.schema.columns.modifyTime.name; - } else if (this.schema.columns.dateModified != null) { - updateAuditColumnName = this.schema.columns.dateModified.name; - } - - this.#query = { - query: { - $case: "insert", - insert: { - schema: this.schema.dbSchema, - table: this.schema.tableName, - columnValue: Object.entries(input) - .filter( - ([key, value]) => - value !== undefined && - key !== "createDate" && - key !== "modifyDate" - ) - .map(([key, value]) => ({ - column: this.schema.columns[key]?.name ?? key, - value: this.toRelationalValue(key, value), - })), - idTable: this.schema.tableName, - idColumn: this.schema.idColumn ?? undefined, - idSequence: this.schema.idSequence ?? undefined, - }, - }, - }; - - if (tableHasCreateAudit && this.#query.query?.$case === "insert") { - this.#query.query.insert.columnValue.push( - { - column: createAuditColumnName, - value: { - value: { - $case: "datetimeValue", - datetimeValue: "CURRENT", - }, - }, - }, - { - column: updateAuditColumnName, - value: { - value: { - $case: "datetimeValue", - datetimeValue: "CURRENT", - }, - }, - } - ); - } - - return { - build: this.build.bind(this), - }; - } - - public update>( - input: UpdateInput - ): Update { - let updateAuditColumnName = "modify_date"; - let auditColumnKey = "modifyDate"; - - if (this.schema.columns.modifyDate != null) { - updateAuditColumnName = this.schema.columns.modifyDate.name; - } else if (this.schema.columns.modifyTime != null) { - updateAuditColumnName = this.schema.columns.modifyTime.name; - auditColumnKey = "modifyTime"; - } else if (this.schema.columns.dateModified != null) { - updateAuditColumnName = this.schema.columns.dateModified.name; - auditColumnKey = "dateModified"; - } - - this.#query = { - query: { - $case: "update", - update: { - schema: this.schema.dbSchema, - table: this.schema.tableName, - columnValue: [ - { - column: updateAuditColumnName, - value: { - value: { - $case: "datetimeValue", - datetimeValue: "CURRENT", - }, - }, - }, - ...Object.entries(input) - .filter( - ([key, value]) => value !== undefined && key !== auditColumnKey - ) - .map(([key, value]) => ({ - column: this.schema.columns[key]?.name ?? key, - value: this.toRelationalValue(key, value), - })), - ], - where: [], - }, - }, - }; - - return { - where: this.whereForModify.bind(this), - build: this.build.bind(this), - }; - } - - public delete(): Delete { - this.#query = { - query: { - $case: "delete", - delete: { - schema: this.schema.dbSchema, - table: this.schema.tableName, - where: [], - }, - }, - }; - - return { - where: this.whereForModify.bind(this), - }; - } - - // private orderBy(column: TableColumn, direction: "asc" | "desc") {} - - private where(...inputs: WhereCondition): Where { - if (this.#query?.query?.$case !== "select") { - throw new Error("Where can only be used in select queries"); - } - - if (!inputs.length) { - throw new Error("Where requires at least one argument"); - } - - if (this.isScanCriteria(inputs[0])) { - for (const input of inputs as ScanCriteria[]) { - this.#query.query.select.where.push({ - key: this.schema.columns[input.key].name, - // TODO: map from @topcoder-framework/lib-common Operator to RelationalOperator - operator: - input.operator === LibCommonOperator.OPERATOR_NOT_EQUAL - ? Operator.OPERATOR_NOT_EQUAL - : Operator.OPERATOR_EQUAL, - value: this.toRelationalValue(input.key, input.value), - }); - } - return { - andWhere: this.where.bind(this), - // orderBy: this.orderBy.bind(this), - limit: this.limit.bind(this), - offset: this.offset.bind(this), - build: this.build.bind(this), - }; - } - - const [column, operator, value] = inputs as [ - TableColumn, - Operator, - RelationalValue - ]; - - this.#query.query.select.where.push({ - key: column.name, - operator, - value, - }); - - return { - andWhere: this.where.bind(this), - // orderBy: this.orderBy.bind(this), - limit: this.limit.bind(this), - offset: this.offset.bind(this), - build: this.build.bind(this), - }; - } - - private whereForModify(...inputs: WhereCondition): WhereForModify { - if (!inputs.length) { - throw new Error("Where requires at least one argument"); - } - - if (this.#query?.query?.$case === "update") { - if (this.isScanCriteria(inputs[0])) { - for (const input of inputs as ScanCriteria[]) { - this.#query.query.update.where.push({ - key: this.schema.columns[input.key].name, - operator: - input.operator === LibCommonOperator.OPERATOR_NOT_EQUAL - ? Operator.OPERATOR_NOT_EQUAL - : Operator.OPERATOR_EQUAL, - value: this.toRelationalValue(input.key, input.value), - }); - } - return { - andWhere: this.whereForModify.bind(this), - build: this.build.bind(this), - }; - } - - const [column, operator, value] = inputs as [ - TableColumn, - Operator, - RelationalValue - ]; - - this.#query.query.update.where.push({ - key: column.name, - operator, - value, - }); - - return { - andWhere: this.whereForModify.bind(this), - build: this.build.bind(this), - }; - } - - if (this.#query?.query?.$case === "delete") { - if (this.isScanCriteria(inputs[0])) { - for (const input of inputs as ScanCriteria[]) { - this.#query.query.delete.where.push({ - key: this.schema.columns[input.key].name, - operator: - input.operator === LibCommonOperator.OPERATOR_NOT_EQUAL - ? Operator.OPERATOR_NOT_EQUAL - : Operator.OPERATOR_EQUAL, - value: this.toRelationalValue(input.key, input.value), - }); - } - return { - andWhere: this.whereForModify.bind(this), - build: this.build.bind(this), - }; - } - - const [column, operator, value] = inputs as [ - TableColumn, - Operator, - RelationalValue - ]; - - this.#query.query.delete.where.push({ - key: column.name, - operator, - value, - }); - - return { - andWhere: this.whereForModify.bind(this), - build: this.build.bind(this), - }; - } - - return { - andWhere: this.whereForModify.bind(this), - build: this.build.bind(this), - }; - } - - private limit(limit: number): Limit { - if (this.#query?.query?.$case != "select") { - throw new Error("Cannot set limit on a non-select query"); - } - this.#query.query.select.limit = limit; - - return { - offset: this.offset.bind(this), - build: this.build.bind(this), - }; - } - - private offset(offset: number): Build { - if (this.#query?.query?.$case != "select") { - throw new Error("Cannot set offset on a non-select query"); - } - this.#query.query.select.offset = offset; - - return { - build: this.build.bind(this), - }; - } - - private build(): Query { - if (!this.#query) { - throw new Error("Query has not been built yet."); - } - - return this.#query; - } - - private isScanCriteria(criteria: unknown): criteria is ScanCriteria { - return ( - criteria != null && - typeof criteria === "object" && - typeof (criteria as ScanCriteria).key === "string" && - typeof (criteria as ScanCriteria).operator !== "undefined" - ); - } - - private isWKTValue(value: unknown): value is Value { - return ( - typeof value === "object" && - value != null && - typeof (value as Value)?.kind != undefined && - typeof (value as Value)?.kind?.$case === "string" - ); - } - - private toRelationalValue(key: string, value: unknown): RelationalValue { - const dataType: ColumnType = this.schema.columns[key].type; - - if (this.isWKTValue(value)) { - value = Value.unwrap(value); - } - - if (this.isWKTValue(value)) { - value = Value.unwrap(value); - } - - if (dataType == null) { - throw new Error(`Unknown column ${key}`); - } - - if (dataType === ColumnType.COLUMN_TYPE_INT) { - return { value: { $case: "intValue", intValue: value as number } }; - } - - if (dataType === ColumnType.COLUMN_TYPE_LONG) { - return { value: { $case: "longValue", longValue: value as number } }; - } - - if (dataType === ColumnType.COLUMN_TYPE_FLOAT) { - return { value: { $case: "floatValue", floatValue: value as number } }; - } - - if (dataType === ColumnType.COLUMN_TYPE_DOUBLE) { - return { value: { $case: "doubleValue", doubleValue: value as number } }; - } - - if (dataType === ColumnType.COLUMN_TYPE_DATE) { - return { value: { $case: "dateValue", dateValue: value as string } }; - } - - if (dataType == ColumnType.COLUMN_TYPE_DATETIME) { - return { - value: { $case: "datetimeValue", datetimeValue: value as string }, - }; - } - - if (dataType == ColumnType.COLUMN_TYPE_STRING) { - return { value: { $case: "stringValue", stringValue: value as string } }; - } - - if (dataType == ColumnType.COLUMN_TYPE_BOOLEAN) { - return { - value: { $case: "booleanValue", booleanValue: value as boolean }, - }; - } - - throw new Error(`Unsupported data type ${dataType}`); - } -} diff --git a/clients/client-relational/src/query/QueryRunner.ts b/clients/client-relational/src/query/QueryRunner.ts index 77261cf..7330fcc 100644 --- a/clients/client-relational/src/query/QueryRunner.ts +++ b/clients/client-relational/src/query/QueryRunner.ts @@ -14,8 +14,10 @@ export interface Transaction { rollback(): void; } +export type RowValueType = string | number | boolean | null; + export interface QueryResult { - rows?: { [key: string]: any }[]; + rows?: { [key: string]: RowValueType }[]; lastInsertId?: number; affectedRows?: number; } @@ -81,7 +83,10 @@ export class QueryRunner { }; } - private unwrapValue(value: RelationalValue): number | string | boolean { + private unwrapValue(value: RelationalValue): RowValueType { + if (_.isUndefined(value.value)) { + return null; + } switch (value.value?.$case) { case "intValue": return value.value.intValue; @@ -111,7 +116,7 @@ export class QueryRunner { switch (queryResponse.result?.$case) { case "rawResult": { const rows = queryResponse.result.rawResult?.rows.map(({ values }) => { - const retObject: { [key: string]: number | string | boolean } = {}; + const retObject: { [key: string]: RowValueType } = {}; Object.entries(values).forEach(([key, value]) => { retObject[key] = this.unwrapValue(value); }); @@ -125,7 +130,7 @@ export class QueryRunner { case "selectResult": return { rows: queryResponse.result.selectResult.rows.map(({ values }) => { - const retObject: { [key: string]: number | string | boolean } = {}; + const retObject: { [key: string]: RowValueType } = {}; column.forEach((col) => { retObject[_.camelCase(col.name)] = this.unwrapValue( @@ -137,7 +142,30 @@ export class QueryRunner { }), }; case "insertResult": - return { lastInsertId: queryResponse.result.insertResult.lastInsertId }; + switch (queryResponse.result.insertResult.insertResultType?.$case) { + case "affectedRows": + return { + affectedRows: + queryResponse.result.insertResult.insertResultType.affectedRows, + }; + case "lastInsertId": + return { + lastInsertId: + queryResponse.result.insertResult.insertResultType.lastInsertId, + }; + case "row": + return { + rows: [ + _.mapValues( + queryResponse.result.insertResult.insertResultType.row.values, + (v) => this.unwrapValue(v) + ), + ], + }; + default: + throw new Error("Unexpected query insert result"); + } + case "updateResult": return { affectedRows: queryResponse.result.updateResult.affectedRows }; case "deleteResult": diff --git a/clients/client-relational/src/query/index.ts b/clients/client-relational/src/query/index.ts index f0c758a..0ab54ff 100644 --- a/clients/client-relational/src/query/index.ts +++ b/clients/client-relational/src/query/index.ts @@ -1,2 +1,2 @@ export * from "./QueryRunner"; -export * from "./QueryBuilder"; +export * from "./Model"; diff --git a/domains/domain-acl/bin/gen-stubs.js b/domains/domain-acl/bin/gen-stubs.js index d186b3e..77c7047 100644 --- a/domains/domain-acl/bin/gen-stubs.js +++ b/domains/domain-acl/bin/gen-stubs.js @@ -37,6 +37,7 @@ const protoConfig = [ `--ts_proto_opt=Mgoogle/protobuf/empty.proto=@topcoder-framework/lib-common`, `--proto_path ${PROTO_DIR} ${PROTO_DIR}/domain-layer/legacy/*.proto`, `--proto_path ${PROTO_DIR} ${PROTO_DIR}/domain-layer/legacy/services/*.proto`, + `--experimental_allow_proto3_optional`, ]; // https://github.com/stephenh/ts-proto#usage diff --git a/domains/domain-challenge/bin/gen-stubs.js b/domains/domain-challenge/bin/gen-stubs.js index 835f2d2..7bc543a 100644 --- a/domains/domain-challenge/bin/gen-stubs.js +++ b/domains/domain-challenge/bin/gen-stubs.js @@ -38,6 +38,7 @@ const protoConfig = [ `--ts_proto_opt=Mgoogle/protobuf/empty.proto=@topcoder-framework/lib-common`, `--proto_path ${PROTO_DIR} ${PROTO_DIR}/domain-layer/challenge/*.proto`, `--proto_path ${PROTO_DIR} ${PROTO_DIR}/domain-layer/challenge/services/*.proto`, + `--experimental_allow_proto3_optional`, ]; // https://github.com/stephenh/ts-proto#usage diff --git a/lib/lib-common/bin/gen-stubs.js b/lib/lib-common/bin/gen-stubs.js index 3966fd8..301c49c 100644 --- a/lib/lib-common/bin/gen-stubs.js +++ b/lib/lib-common/bin/gen-stubs.js @@ -34,6 +34,7 @@ const protoConfig = [ `--ts_proto_opt=outputClientImpl=true`, `--ts_proto_out=${MODEL_DIR}`, `--proto_path ${PROTO_DIR} ${PROTO_DIR}/common/*.proto`, + `--experimental_allow_proto3_optional`, ]; // https://github.com/stephenh/ts-proto#usage diff --git a/lib/lib-common/src/models/google/protobuf/empty.ts b/lib/lib-common/src/models/google/protobuf/empty.ts index 4414abb..5187163 100644 --- a/lib/lib-common/src/models/google/protobuf/empty.ts +++ b/lib/lib-common/src/models/google/protobuf/empty.ts @@ -9,6 +9,8 @@ import _m0 from "protobufjs/minimal"; * service Foo { * rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); * } + * + * The JSON representation for `Empty` is empty JSON object `{}`. */ export interface Empty {} diff --git a/lib/lib-common/src/models/google/protobuf/struct.ts b/lib/lib-common/src/models/google/protobuf/struct.ts index 3385280..c2ebeca 100644 --- a/lib/lib-common/src/models/google/protobuf/struct.ts +++ b/lib/lib-common/src/models/google/protobuf/struct.ts @@ -58,8 +58,8 @@ export interface Struct_FieldsEntry { /** * `Value` represents a dynamically typed value which can be either * null, a number, a string, a boolean, a recursive struct value, or a - * list of values. A producer of value is expected to set one of these - * variants. Absence of any variant indicates an error. + * list of values. A producer of value is expected to set one of that + * variants, absence of any variant indicates an error. * * The JSON representation for `Value` is JSON value. */ diff --git a/lib/lib-common/src/models/google/protobuf/timestamp.ts b/lib/lib-common/src/models/google/protobuf/timestamp.ts index e4a5adf..66f8734 100644 --- a/lib/lib-common/src/models/google/protobuf/timestamp.ts +++ b/lib/lib-common/src/models/google/protobuf/timestamp.ts @@ -53,15 +53,7 @@ import _m0 from "protobufjs/minimal"; * Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) * .setNanos((int) ((millis % 1000) * 1000000)).build(); * - * Example 5: Compute Timestamp from Java `Instant.now()`. - * - * Instant now = Instant.now(); - * - * Timestamp timestamp = - * Timestamp.newBuilder().setSeconds(now.getEpochSecond()) - * .setNanos(now.getNano()).build(); - * - * Example 6: Compute Timestamp from current time in Python. + * Example 5: Compute Timestamp from current time in Python. * * timestamp = Timestamp() * timestamp.GetCurrentTime() diff --git a/scripts/packages/format.sh b/scripts/packages/format.sh index 0aefcb8..bc8deed 100755 --- a/scripts/packages/format.sh +++ b/scripts/packages/format.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash -yarn prettier --write src/**/*.ts +yarn prettier --write "src/**/*.ts"