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 import std.exception: enforce; 37 38 //TODO: it is possible to skip this check for fixed-size values? 39 enforce(data.length <= int.max, `data.length is too big for use as Postgres value`); 40 41 this._data = data; 42 this._format = format; 43 this._oidType = oidType; 44 this._isNull = isNull; 45 } 46 47 /// Null Value constructor 48 this(in ValueFormat format, in OidType oidType) pure 49 { 50 this._format = format; 51 this._oidType = oidType; 52 } 53 54 @safe const pure nothrow @nogc 55 { 56 /// Indicates if the value is NULL 57 bool isNull() 58 { 59 return _isNull; 60 } 61 62 /// Indicates if the value is array type 63 bool isArray() 64 { 65 return dpq2.oids.isSupportedArray(oidType); 66 } 67 alias isSupportedArray = isArray; //TODO: deprecate 68 69 /// Returns Oid of the value 70 OidType oidType() 71 { 72 return _oidType; 73 } 74 75 /// Returns ValueFormat representation (text or binary) 76 ValueFormat format() 77 { 78 return _format; 79 } 80 } 81 82 package void oidType(OidType type) @safe pure nothrow @nogc 83 { 84 _oidType = type; 85 } 86 87 immutable(ubyte)[] data() pure const 88 { 89 import std.exception; 90 import core.exception; 91 92 enforce!AssertError(!isNull, "Attempt to read NULL value", __FILE__, __LINE__); 93 94 return _data; 95 } 96 97 /// 98 string toString() const @trusted 99 { 100 import vibe.data.bson: Bson; 101 import dpq2.conv.to_bson; 102 import std.conv: to; 103 104 return this.as!Bson.toString~"::"~oidType.to!string~"("~(format == ValueFormat.TEXT? "t" : "b")~")"; 105 } 106 } 107 108 @system unittest 109 { 110 import dpq2.conv.to_d_types; 111 import core.exception: AssertError; 112 113 Value v = Value(ValueFormat.BINARY, OidType.Int4); 114 115 bool exceptionFlag = false; 116 117 try 118 cast(void) v.as!int; 119 catch(AssertError e) 120 exceptionFlag = true; 121 122 assert(exceptionFlag); 123 } 124 125 /// 126 enum ValueFormat : int { 127 TEXT, /// 128 BINARY /// 129 } 130 131 import std.conv: to, ConvException; 132 133 /// Conversion exception types 134 enum ConvExceptionType 135 { 136 NOT_ARRAY, /// Format of the value isn't array 137 NOT_BINARY, /// Format of the column isn't binary 138 NOT_TEXT, /// Format of the column isn't text string 139 NOT_IMPLEMENTED, /// Support of this type isn't implemented (or format isn't matches to specified D type) 140 SIZE_MISMATCH, /// Value size is not matched to the Postgres value or vice versa 141 CORRUPTED_JSONB, /// Corrupted JSONB value 142 DATE_VALUE_OVERFLOW, /// Date value isn't fits to Postgres binary Date value 143 DIMENSION_MISMATCH, /// Array dimension size is not matched to the Postgres array 144 CORRUPTED_ARRAY, /// Corrupted array value 145 OUT_OF_RANGE, /// Index is out of range 146 } 147 148 /// Value conversion exception 149 class ValueConvException : ConvException 150 { 151 const ConvExceptionType type; /// Exception type 152 153 this(ConvExceptionType t, string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) pure @safe 154 { 155 type = t; 156 super(msg, file, line, next); 157 } 158 } 159 160 package void throwTypeComplaint(OidType receivedType, string expectedType, string file = __FILE__, size_t line = __LINE__) pure 161 { 162 throw new ValueConvException( 163 ConvExceptionType.NOT_IMPLEMENTED, 164 "Format of the column ("~to!string(receivedType)~") doesn't match to D native "~expectedType, 165 file, line 166 ); 167 }