1 ///
2 module dpq2.value;
3 
4 import dpq2.oids;
5 
6 @safe:
7 
8 /**
9 Represents table cell or argument value
10 
11 Internally it is a ubyte[].
12 If it returned by Answer methods it contains a reference to the data of
13 the server answer and it can not be accessed after Answer is destroyed.
14 */
15 struct Value
16 {
17     private
18     {
19         bool _isNull = true;
20         OidType _oidType = OidType.Undefined;
21 
22         ValueFormat _format;
23     }
24 
25     package immutable(ubyte)[] _data;
26 
27     // FIXME:
28     // The pointer returned by PQgetvalue points to storage that is part of the PGresult structure.
29     // One should not modify the data it points to, and one must explicitly copy the data into other
30     // storage if it is to be used past the lifetime of the PGresult structure itself.
31     // Thus, it is need to store reference to Answer here to ensure that result is still available.
32     // (Also see DIP1000)
33     /// ctor
34     this(immutable(ubyte)[] data, in OidType oidType, bool isNull = false, in ValueFormat format = ValueFormat.BINARY) inout pure
35     {
36         this._data = data;
37         this._format = format;
38         this._oidType = oidType;
39         this._isNull = isNull;
40     }
41 
42     /// Null Value constructor
43     this(in ValueFormat format, in OidType oidType) pure
44     {
45         this._format = format;
46         this._oidType = oidType;
47     }
48 
49     @safe const pure nothrow @nogc
50     {
51         /// Indicates if the value is NULL
52         bool isNull()
53         {
54             return _isNull;
55         }
56 
57         /// Indicates if the value is array type
58         bool isArray()
59         {
60             return dpq2.oids.isSupportedArray(oidType);
61         }
62         alias isSupportedArray = isArray; //TODO: deprecate
63 
64         /// Returns Oid of the value
65         OidType oidType()
66         {
67             return _oidType;
68         }
69 
70         /// Returns ValueFormat representation (text or binary)
71         ValueFormat format()
72         {
73             return _format;
74         }
75     }
76 
77     package void oidType(OidType type) @safe pure nothrow @nogc
78     {
79         _oidType = type;
80     }
81 
82     immutable(ubyte)[] data() pure const
83     {
84         import std.exception;
85         import core.exception;
86 
87         enforceEx!AssertError(!isNull, "Attempt to read NULL value", __FILE__, __LINE__);
88 
89         return _data;
90     }
91 
92     ///
93     string toString() const @trusted
94     {
95         import vibe.data.bson: Bson;
96         import dpq2.conv.to_bson;
97         import std.conv: to;
98 
99         return this.as!Bson.toString~"::"~oidType.to!string~"("~(format == ValueFormat.TEXT? "t" : "b")~")";
100     }
101 }
102 
103 @system unittest
104 {
105     import dpq2.conv.to_d_types;
106     import core.exception: AssertError;
107 
108     Value v = Value(ValueFormat.BINARY, OidType.Int4);
109 
110     bool exceptionFlag = false;
111 
112     try
113         cast(void) v.as!int;
114     catch(AssertError e)
115         exceptionFlag = true;
116 
117     assert(exceptionFlag);
118 }
119 
120 ///
121 enum ValueFormat : int {
122     TEXT, ///
123     BINARY ///
124 }
125 
126 import std.conv: to, ConvException;
127 
128 /// Conversion exception types
129 enum ConvExceptionType
130 {
131     NOT_ARRAY, /// Format of the value isn't array
132     NOT_BINARY, /// Format of the column isn't binary
133     NOT_TEXT, /// Format of the column isn't text string
134     NOT_IMPLEMENTED, /// Support of this type isn't implemented (or format isn't matches to specified D type)
135     SIZE_MISMATCH, /// Value size is not matched to the Postgres value
136     CORRUPTED_JSONB, /// Corrupted JSONB value
137     DATE_VALUE_OVERFLOW, /// Date value isn't fits to Postgres binary Date value
138 }
139 
140 /// Value conversion exception
141 class ValueConvException : ConvException
142 {
143     const ConvExceptionType type; /// Exception type
144 
145     this(ConvExceptionType t, string msg, string file, size_t line) pure @safe
146     {
147         type = t;
148         super(msg, file, line);
149     }
150 }
151 
152 package void throwTypeComplaint(OidType receivedType, string expectedType, string file, size_t line) pure
153 {
154     throw new ValueConvException(
155             ConvExceptionType.NOT_IMPLEMENTED,
156             "Format of the column ("~to!string(receivedType)~") doesn't match to D native "~expectedType,
157             file, line
158         );
159 }