diff --git a/decode.ts b/decode.ts index 15c3c1cc..a1b909f8 100644 --- a/decode.ts +++ b/decode.ts @@ -5,6 +5,8 @@ import { decodeBigintArray, decodeBoolean, decodeBooleanArray, + decodeBox, + decodeBoxArray, decodeBytea, decodeByteaArray, decodeDate, @@ -102,6 +104,10 @@ function decodeText(value: Uint8Array, typeOid: number): any { return decodeBoolean(strValue); case Oid.bool_array: return decodeBooleanArray(strValue); + case Oid.box: + return decodeBox(strValue); + case Oid.box_array: + return decodeBoxArray(strValue); case Oid.bytea: return decodeBytea(strValue); case Oid.byte_array: diff --git a/oid.ts b/oid.ts index 6728ecf9..cc6fae2d 100644 --- a/oid.ts +++ b/oid.ts @@ -45,8 +45,7 @@ export const Oid = { lseg: 601, // deno-lint-ignore camelcase _path_0: 602, - // deno-lint-ignore camelcase - _box_0: 603, + box: 603, // deno-lint-ignore camelcase _polygon_0: 604, line: 628, @@ -115,7 +114,7 @@ export const Oid = { // deno-lint-ignore camelcase _path_1: 1019, // deno-lint-ignore camelcase - _box_1: 1020, + box_array: 1020, // deno-lint-ignore camelcase float4_array: 1021, // deno-lint-ignore camelcase diff --git a/query/array_parser.ts b/query/array_parser.ts index bf1765e9..0cc06bb8 100644 --- a/query/array_parser.ts +++ b/query/array_parser.ts @@ -1,25 +1,5 @@ -// Ported from https://github.com/bendrucker/postgres-array -// The MIT License (MIT) -// -// Copyright (c) Ben Drucker (bendrucker.me) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. +// Based of https://github.com/bendrucker/postgres-array +// Copyright (c) Ben Drucker (bendrucker.me). MIT License. /** Incorrectly parsed data types default to null */ type ArrayResult = Array>; @@ -93,7 +73,23 @@ class ArrayParser { } } + /** + * Arrays can contain items separated by semicolon (such as boxes) + * and commas + * + * This checks if there is an instance of a semicolon on the top level + * of the array. If it were to be found, the separator will be + * a semicolon, otherwise it will default to a comma + */ + getSeparator() { + if (/;(?![^(]*\))/.test(this.source.substr(1, this.source.length - 1))) { + return ";"; + } + return ","; + } + parse(nested = false): ArrayResult { + const separator = this.getSeparator(); let character, parser, quote; this.consumeDimensions(); while (!this.isEof()) { @@ -117,7 +113,7 @@ class ArrayParser { } else if (character.value === '"' && !character.escaped) { if (quote) this.newEntry(true); quote = !quote; - } else if (character.value === "," && !quote) { + } else if (character.value === separator && !quote) { this.newEntry(); } else { this.record(character.value); diff --git a/query/decoders.ts b/query/decoders.ts index 42077465..55da3014 100644 --- a/query/decoders.ts +++ b/query/decoders.ts @@ -1,5 +1,5 @@ import { parseArray } from "./array_parser.ts"; -import { Float8, Line, LineSegment, Point, TID } from "./types.ts"; +import { Box, Float8, Line, LineSegment, Point, TID } from "./types.ts"; // Datetime parsing based on: // https://github.com/bendrucker/postgres-date/blob/master/index.js @@ -29,6 +29,19 @@ export function decodeBooleanArray(value: string) { return parseArray(value, (x) => x[0] === "t"); } +export function decodeBox(value: string): Box { + const [a, b] = value.match(/\(.*?\)/g) || []; + + return { + a: decodePoint(a), + b: decodePoint(b), + }; +} + +export function decodeBoxArray(value: string) { + return parseArray(value, decodeBox); +} + export function decodeBytea(byteaStr: string): Uint8Array { if (HEX_PREFIX_REGEX.test(byteaStr)) { return decodeByteaHex(byteaStr); diff --git a/query/types.ts b/query/types.ts index a62ad589..194d2d86 100644 --- a/query/types.ts +++ b/query/types.ts @@ -1,3 +1,11 @@ +/** + * https://www.postgresql.org/docs/13/datatype-geometric.html#id-1.5.7.16.8 + */ +export interface Box { + a: Point; + b: Point; +} + /** * Decimal-like string. Uses dot to split the decimal * diff --git a/test_deps.ts b/test_deps.ts index 8c75f43b..4becc50e 100644 --- a/test_deps.ts +++ b/test_deps.ts @@ -12,4 +12,4 @@ export { export { format as formatDate, parse as parseDate, -} from "https://deno.land/std@0.85.0/datetime/mod.ts"; +} from "https://deno.land/std@0.84.0/datetime/mod.ts"; diff --git a/tests/data_types.ts b/tests/data_types.ts index 41869aec..2ebb4253 100644 --- a/tests/data_types.ts +++ b/tests/data_types.ts @@ -9,6 +9,7 @@ import { Client } from "../mod.ts"; import TEST_CONNECTION_PARAMS from "./config.ts"; import { getTestClient } from "./helpers.ts"; import { + Box, Float4, Float8, Line, @@ -764,3 +765,31 @@ testClient(async function lineSegmentArray() { }, ]); }); + +testClient(async function box() { + const result = await CLIENT.queryArray<[Box]>( + "SELECT '((1, 2), (3, 4))'::BOX", + ); + + assertEquals(result.rows[0][0], { + a: { x: "3", y: "4" }, + b: { x: "1", y: "2" }, + }); +}); + +testClient(async function boxArray() { + const result = await CLIENT.queryArray<[[Box, Box]]>( + "SELECT ARRAY['(1, 2), (3, 4)'::BOX, '41, 1, -9, 25.5']", + ); + + assertEquals(result.rows[0][0], [ + { + a: { x: "3", y: "4" }, + b: { x: "1", y: "2" }, + }, + { + a: { x: "41", y: "25.5" }, + b: { x: "-9", y: "1" }, + }, + ]); +});