Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for all native user data types PostgreSQL 15 #193

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/dpq2/conv/arrays.d
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,17 @@ static assert(!isStaticArrayString!(ubyte[2]));
template isArrayType(T)
{
import dpq2.conv.geometric : isValidPolygon;
import dpq2.conv.ranges : isMultiRange;
import std.traits : Unqual;

enum isArrayType = isArray!T && !isAssociativeArray!T && !isValidPolygon!T && !is(Unqual!(ElementType!T) == ubyte) && !isSomeString!T
&& !isStaticArrayString!T;
enum isArrayType = isArray!T && !(
isAssociativeArray!T ||
isMultiRange!T ||
isValidPolygon!T ||
is(Unqual!(ElementType!T) == ubyte) ||
isSomeString!T ||
isStaticArrayString!T
);
}

static assert(isArrayType!(int[]));
Expand Down
50 changes: 50 additions & 0 deletions src/dpq2/conv/bit.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
module dpq2.conv.bit;

import dpq2.conv.to_d_types;
import dpq2.oids : OidType;
import dpq2.value;

import std.bitmanip : bigEndianToNative;
import std.conv : to;
import std.exception : enforce;
import std.traits : hasMember;


template isBitString(T) {
enum isBitString = hasMember!(T, "bits") && __traits(compiles, typeof(T.bits));
}

struct BitString {
uint stringLen;
ubyte[] bits;

immutable(ubyte)[] _data;

this(immutable(ubyte)[] binaryData) {
enforce(binaryData.length >= uint.sizeof, "cannot construct bit string with insufficient data");

this._data = binaryData;

this.stringLen = binaryData[0..uint.sizeof].bigEndianToNative!uint;

binaryData = binaryData[uint.sizeof..$];

assert(binaryData.length >= this.byteLen, "data shorter than bit string length");
assert(this.stringLen, "zero bit string length?");

this.bits = binaryData[0..this.byteLen].dup;
}

auto byteLen() @property { return (this.stringLen + 7) / 8; }

auto rawData() @property { return _data.dup; }
}

package:

/// Convert Value to native network address type
N binaryValueAs(N)(in Value v) @trusted
if (isBitString!N)
{
return N(v.data);
}
87 changes: 78 additions & 9 deletions src/dpq2/conv/geometric.d
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,10 @@ template isValidPolygon(T)
{
static if (is(T == Nullable!R, R))
enum isValidPolygon = false;
else static if (is(T == Point[]))
enum isValidPolygon = false;
else
enum isValidPolygon = isArray!T && isValidPointType!(ElementType!T);
enum isValidPolygon = isArray!T && isValidPointType!(ElementType!T) || __traits(isSame, TemplateOf!T, Polygon);
}

unittest
Expand Down Expand Up @@ -158,6 +160,73 @@ if(isValidBoxType!Box)
return createValue(data, OidType.Box);
}


struct Point
{
double x, y;
}

struct Box
{
Point min, max;
}

struct LineSegment(Point)
{
Point[2] data;

this(Point start, Point end) {
this.data[0] = start;
this.data[1] = end;
}

auto start() @ property { return data[0]; }
auto end() @ property { return data[1]; }
}

struct Polygon(Point)
{
Point[] data;

auto opIndex(size_t idx)
{
assert(idx < data.length, "index out of bounds");
return data[idx];
}

auto opIndexAssign(Point value, size_t idx)
{
if (idx >= data.length) data.length = idx + 1;
data[idx] = value;

return this;
}

int opApply(scope int delegate(size_t, ref Point) dg) @trusted
{
foreach (i, val; data) {
if (auto result = dg(i, val))
return result;
}
return 0;
}

int opApplyReverse(scope int delegate(size_t, ref Point) dg) @trusted
{
foreach_reverse (i, val; data) {
if (auto result = dg(i, val))
return result;
}
return 0;
}

void opAssign(Point[] value) { data = value.dup; }

auto length() @property { return data.length; }

Point front() @property { return Point(); }
}

/// Infinite line - {A,B,C} (Ax + By + C = 0)
struct Line
{
Expand Down Expand Up @@ -242,7 +311,7 @@ if(isValidPolygon!Polygon)
ubyte[] data = new ubyte[poly.length * 16 + 4];
auto rem = (cast(int)poly.length).nativeToBigEndian[0 .. $].copy(data);

foreach (ref p; poly)
foreach (i, ref p; poly)
rem = p.serializePoint(rem);

return createValue(data, OidType.Polygon);
Expand Down Expand Up @@ -399,7 +468,7 @@ package mixin template GeometricInstancesForIntegrationTest()
@safe:

import gfm.math;
import dpq2.conv.geometric: Circle, Path;
import dpq2.conv.geometric: Circle, Path, Polygon;

alias Point = vec2d;
alias Box = box2d;
Expand All @@ -418,7 +487,7 @@ package mixin template GeometricInstancesForIntegrationTest()
}
}
alias TestPath = Path!Point;
alias Polygon = Point[];
alias TestPolygon = Polygon!Point;
alias TestCircle = Circle!Point;
}

Expand Down Expand Up @@ -447,8 +516,8 @@ unittest
p = TestPath(true, [Point(1,1), Point(2,2)]);
assert(p.toValue.binaryValueAs!TestPath == p);

Polygon poly = [Point(1,1), Point(2,2), Point(3,3)];
assert(poly.toValue.binaryValueAs!Polygon == poly);
TestPolygon poly = TestPolygon([Point(1,1), Point(2,2), Point(3,3)]);
assert(poly.toValue.binaryValueAs!TestPolygon == poly);

auto c = TestCircle(Point(1,2), 3);
assert(c.toValue.binaryValueAs!TestCircle == c);
Expand Down Expand Up @@ -480,7 +549,7 @@ unittest

v = [Point(1,1), Point(2,2)].toValue;
v.oidType = OidType.Text;
assertThrown!ValueConvException(v.binaryValueAs!Polygon);
assertThrown!ValueConvException(v.binaryValueAs!TestPolygon);

v = TestCircle(Point(1,1), 3).toValue;
v.oidType = OidType.Text;
Expand Down Expand Up @@ -515,9 +584,9 @@ unittest

v = [Point(1,1), Point(2,2)].toValue;
v._data.length -= 16;
assertThrown!ValueConvException(v.binaryValueAs!Polygon);
assertThrown!ValueConvException(v.binaryValueAs!TestPolygon);
v._data.length = 1;
assertThrown!ValueConvException(v.binaryValueAs!Polygon);
assertThrown!ValueConvException(v.binaryValueAs!TestPolygon);

v = TestCircle(Point(1,1), 3).toValue;
v._data.length = 1;
Expand Down
2 changes: 1 addition & 1 deletion src/dpq2/conv/native_tests.d
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public void _integration_test( string connParam ) @system
C!Box(Box(Point(1,2),Point(3,4)), "box", "'(3,4),(1,2)'"); // PG handles box ordered as upper right first and lower left next
C!TestPath(TestPath(true, [Point(1,1), Point(2,2), Point(3,3)]), "path", "'((1,1),(2,2),(3,3))'");
C!TestPath(TestPath(false, [Point(1,1), Point(2,2), Point(3,3)]), "path", "'[(1,1),(2,2),(3,3)]'");
C!Polygon(([Point(1,1), Point(2,2), Point(3,3)]), "polygon", "'((1,1),(2,2),(3,3))'");
C!TestPolygon(TestPolygon([Point(1,1), Point(2,2), Point(3,3)]), "polygon", "'((1,1),(2,2),(3,3))'");
C!TestCircle(TestCircle(Point(1,2), 10), "circle", "'<(1,2),10>'");
C!(Nullable!Point)(Nullable!Point(Point(1,2)), "point", "'(1,2)'");

Expand Down
75 changes: 75 additions & 0 deletions src/dpq2/conv/net.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
module dpq2.conv.net;

import dpq2.conv.to_d_types;
import dpq2.oids : OidType;
import dpq2.value;

import std.bitmanip : bigEndianToNative;
import std.conv : to;
import std.exception : enforce;
import std.format : format;
import std.socket : AddressFamily;
import std.traits : hasMember;


enum PG_TYPE : ushort {
INET_ADDR = 0,
CIDR_ADDR = 1
}

template isNetworkAddress(T) {
enum isNetworkAddress = hasMember!(T, "isInet") && __traits(compiles, typeof(T.isInet));
}

struct NetworkAddress {
AddressFamily family;
ubyte netmask;
PG_TYPE type;
ubyte addressLen;
ubyte[] address;

immutable(ubyte)[] _data;

this(immutable(ubyte)[] binaryData) {
enforce(binaryData.length >= uint.sizeof, "cannot construct network address with insufficient data");

this._data = binaryData;

this.family = binaryData[0].to!AddressFamily;
this.netmask = binaryData[1];
this.type = binaryData[2].to!PG_TYPE;
this.addressLen = binaryData[3];

binaryData = binaryData[4..$];

assert(this.addressLen <= binaryData.length, "data shorter than address length");
assert(this.addressLen, "zero address length?");

this.address = binaryData[0..this.addressLen].dup;
}

bool isInet() @property { return type == PG_TYPE.INET_ADDR; }
bool isCidr() @property { return type == PG_TYPE.CIDR_ADDR; }

auto toString() {
switch (this.family) {
case AddressFamily.INET:
return format("%(%d.%)/%d", this.address[0..this.addressLen], this.netmask);

case AddressFamily.INET6:
return format("%(%x:%)/%d", this.address[0..this.addressLen].to!(ushort[]), this.netmask);

default:
return format("NetowrkAddress(%d,%d,%d,%d,%s)", this.family, this.netmask, this.type, this.addressLen, this.address);
}
}
}

package:

/// Convert Value to native network address type
N binaryValueAs(N)(in Value v) @trusted
if (isNetworkAddress!N)
{
return N(v.data);
}
Loading