1 module dpq.value;
2 
3 import dpq.result;
4 import dpq.exception;
5 import dpq.pgarray;
6 import dpq.meta;
7 
8 import derelict.pq.pq;
9 
10 import std.algorithm : map;
11 import std.array;
12 import std.conv : to;
13 import std.typecons : Nullable;
14 import std.bitmanip;
15 import std.traits;
16 import std.datetime : SysTime, DateTime;
17 
18 version(unittest) import std.stdio;
19 
20 enum POSTGRES_EPOCH = DateTime(2000, 1, 1);
21 
22 package enum Type : Oid
23 {
24 	INFER = 0,
25 	BOOL = 16,
26 	BYTEA = 17,
27 	CHAR = 18,
28 	NAME = 19,
29 	INT8 = 20,
30 	INT2 = 21,
31 	INT2VECTOR = 22,
32 	INT4 = 23,
33 	REGPROC = 24,
34 	TEXT = 25,
35 	OID = 26,
36 	TID = 27,
37 	XID = 28,
38 	CID = 29,
39 	OIDVECTOR = 30,
40 	JSON = 114,
41 	XML = 142,
42 	PGNODETREE = 194,
43 	POINT = 600,
44 	LSEG = 601,
45 	PATH = 602,
46 	BOX = 603,
47 	POLYGON = 604,
48 	LINE = 628,
49 	FLOAT4 = 700,
50 	FLOAT8 = 701,
51 	ABSTIME = 702,
52 	RELTIME = 703,
53 	TINTERVAL = 704,
54 	UNKNOWN = 705,
55 	CIRCLE = 718,
56 	CASH = 790,
57 	MACADDR = 829,
58 	INET = 869,
59 	CIDR = 650,
60 	INT2ARRAY = 1005,
61 	INT4ARRAY = 1007,
62 	TEXTARRAY = 1009,
63 	OIDARRAY = 1028,
64 	FLOAT4ARRAY = 1021,
65 	ACLITEM = 1033,
66 	CSTRINGARRAY = 1263,
67 	BPCHAR = 1042,
68 	VARCHAR = 1043,
69 	DATE = 1082,
70 	TIME = 1083,
71 	TIMESTAMP = 1114,
72 	TIMESTAMPTZ = 1184,
73 	INTERVAL = 1186,
74 	TIMETZ = 1266,
75 	BIT = 1560,
76 	VARBIT = 1562,
77 	NUMERIC = 1700,
78 	REFCURSOR = 1790,
79 	REGPROCEDURE = 2202,
80 	REGOPER = 2203,
81 	REGOPERATOR = 2204,
82 	REGCLASS = 2205,
83 	REGTYPE = 2206,
84 	REGTYPEARRAY = 2211,
85 	UUID = 2950,
86 	LSN = 3220,
87 	TSVECTOR = 3614,
88 	GTSVECTOR = 3642,
89 	TSQUERY = 3615,
90 	REGCONFIG = 3734,
91 	REGDICTIONARY = 3769,
92 	JSONB = 3802,
93 	INT4RANGE = 3904,
94 	RECORD = 2249,
95 	RECORDARRAY = 2287,
96 	CSTRING = 2275,
97 	ANY = 2276,
98 	ANYARRAY = 2277,
99 	VOID = 2278,
100 	TRIGGER = 2279,
101 	EVTTRIGGER = 3838,
102 	LANGUAGE_HANDLER = 2280,
103 	INTERNAL = 2281,
104 	OPAQUE = 2282,
105 	ANYELEMENT = 2283,
106 	ANYNONARRAY = 2776,
107 	ANYENUM = 3500,
108 	FDW_HANDLER = 3115,
109 	ANYRANGE = 3831,
110 }
111 
112 struct Value
113 {
114 	private
115 	{
116 		ubyte[] _valueBytes;
117 		int _size;
118 		Type _type;
119 		bool _isNull;
120 	}
121 
122 	this(typeof(null) n)
123 	{
124 		_isNull = true;
125 	}
126 
127 	this(T)(T val)
128 	{
129 		opAssign(val);
130 	}
131 
132 	this(T)(T* val, int len, Type type = Type.INFER)
133 	{
134 		_size = len;
135 		_type = type;
136 
137 		_valueBytes = val[0 .. len].dup;
138 	}
139 
140 	this(Value val)
141 	{
142 		opAssign(val);
143 	}
144 
145 	void opAssign(T)(T val)
146 			if (isArray!T)
147 	{
148 		_size = (ForeachType!T.sizeof * val.length).to!int;
149 
150 		static if (is(T == ubyte[]))
151 			_valueBytes = val;
152 		else
153 		{
154 			_valueBytes = PGArray(val).toBytes();
155 			_size = _valueBytes.length.to!int;
156 		}
157 
158 		_type = typeOid!T;
159 	}
160 
161 	void opAssign(T)(T val)
162 			if(!isArray!T)
163 	{
164 		_size = val.sizeof;
165 
166 		//_valueBytes = new ubyte[_size];
167 		//write(_valueBytes, val, 0);
168 		_valueBytes = nativeToBigEndian(val).dup;
169 
170 		_type = typeOid!T;
171 	}
172 
173 	void opAssign(string val)
174 	{
175 		import std.string;
176 
177 		_valueBytes = val.representation.dup;
178 		_size = _valueBytes.length.to!int;
179 		_type = Type.TEXT;
180 	}
181 
182 	void opAssign(SysTime val)
183 	{
184 		import core.time;
185 
186 		_type = typeOid!SysTime;
187 		// stdTime is in hnsecs, psql wants microsecs
188 		long diff = val.stdTime - SysTime(POSTGRES_EPOCH).stdTime;
189 		_valueBytes = nativeToBigEndian(diff / 10).dup;
190 		_size = typeof(val.stdTime).sizeof;
191 	}
192 	
193 	void opAssign(Value val)
194 	{
195 		_valueBytes = val._valueBytes;
196 		_size = val._size;
197 		_type = val._type;
198 	}
199 
200 	unittest
201 	{
202 		import std.string;
203 
204 		writeln(" * value");
205 		writeln("\t * opAssign");
206 
207 		int a = 0xFFFF_FFFF;
208 		Value v;
209 		v.opAssign(a);
210 
211 		assert(v._size == 4);
212 		assert(v._valueBytes == [255, 255, 255, 255]);
213 		assert(v._type == Type.INT4);
214 		
215 		int[][] b = [[1], [2]];
216 		auto pga = PGArray(b);
217 
218 		v.opAssign(b);
219 		assert(v._size == pga.toBytes().length);
220 		assert(v._valueBytes == pga.toBytes());
221 
222 		string str = "some string, I don't even know.";
223 		v.opAssign(str);
224 
225 		assert(v._valueBytes == str.representation);
226 		assert(v.size == str.representation.length);
227 
228 		Value v2;
229 		v.opAssign(v2);
230 		assert(v2 == v);
231 
232 
233 		import std.datetime;
234 		SysTime t = Clock.currTime;
235 		v2 = t;
236 
237 		assert(v2.as!SysTime == t);
238 	}
239 
240 	@property int size()
241 	{
242 		return _size;
243 	}
244 
245 	@property Oid type()
246 	{
247 		return _type;
248 	}
249 	
250 	@property const(ubyte)* valuePointer()
251 	{
252 		return _valueBytes.ptr;
253 	}
254 
255 	Nullable!T as(T)()
256 	{
257 		if (_isNull)
258 			return Nullable!T.init;
259 
260 		const(ubyte)[] data = _valueBytes[0 .. _size];
261 		return fromBytes!T(data, _size);
262 	}
263 
264 	unittest
265 	{
266 		writeln("\t * as");
267 
268 		Value v = "123";
269 		assert(v.as!string == "123");
270 
271 		v = 123;
272 		assert(v.as!int == 123);
273 
274 		v = [[1, 2], [3, 4]];
275 		assert(v.as!(int[][]) == [[1, 2],[3, 4]]);
276 	}
277 }
278 
279 template typeOid(T)
280 {
281 		alias TU = std.typecons.Unqual!T;
282 		static if (isArray!T && !isSomeString!T)
283 		{
284 			alias BT = BaseType!T;
285 			static if (is(BT == int))
286 				enum typeOid = Type.INT4ARRAY;
287 			else static if (is(BT == short))
288 				enum typeOid = Type.INT2ARRAY;
289 			else static if (is(BT == float))
290 				enum typeOid = Type.FLOAT4ARRAY;
291 			else static if (is(BT == byte) || is (BT == ubyte))
292 				enum typeOid = Type.BYTEA;
293 			else
294 				static assert(false, "Cannot map array type " ~ T.stringof ~ " to Oid");
295 		}
296 		else
297 		{
298 			static if (is(TU == int))
299 				enum typeOid = Type.INT4;
300 			else static if (is(TU == long))
301 				enum typeOid = Type.INT8;
302 			else static if (is(TU == bool))
303 				enum typeOid = Type.BOOL;
304 			else static if (is(TU == byte))
305 				enum typeOid = Type.CHAR;
306 			else static if (is(TU == char))
307 				enum typeOid = Type.CHAR;
308 			else static if (isSomeString!TU)
309 				enum typeOid = Type.TEXT;
310 			else static if (is(TU == short))
311 				enum typeOid = Type.INT2;
312 			else static if (is(TU == float))
313 				enum typeOid = Type.FLOAT4;
314 			else static if (is(TU == double))
315 				enum typeOid = Type.FLOAT8;
316 			else static if (is(TU == SysTime))
317 				enum typeOid = Type.TIMESTAMP;
318 
319 			/**
320 				Since unsigned types are not supported by PostgreSQL, we use signed
321 				types for them. Transfer and representation in D will still work correctly,
322 				but SELECTing them in the psql console, or as a string might result in 
323 				a negative number.
324 
325 				It is recommended not to use unsigned types in structures, that will
326 				be used in the DB directly.
327 			*/
328 			else static if (is(TU == ulong))
329 				enum typeOid = Type.INT8;
330 			else static if (is(TU == uint))
331 				enum typeOid = Type.INT4;
332 			else static if (is(TU == ushort) || is(TU == char))
333 				enum typeOid = Type.INT2;
334 			else static if (is(TU == ubyte))
335 				enum typeOid = Type.CHAR;
336 			else
337 				// Try to infer
338 				enum typeOid = Type.INFER;
339 		}
340 }
341 
342 unittest
343 {
344 	writeln("\t * typeOid");
345 
346 	static assert(typeOid!int == Type.INT4, "int");
347 	static assert(typeOid!string == Type.TEXT, "string");
348 	static assert(typeOid!(int[]) == Type.INT4ARRAY, "int[]");
349 	static assert(typeOid!(int[][]) == Type.INT4ARRAY, "int[][]");
350 	static assert(typeOid!(ubyte[]) == Type.BYTEA, "ubyte[]");
351 }
352 
353 Oid[] paramTypes(Value[] values)
354 {
355 	return array(values.map!(v => v.type));
356 }
357 
358 int[] paramLengths(Value[] values)
359 {
360 	return array(values.map!(v => v.size));
361 }
362 
363 int[] paramFormats(Value[] values)
364 {
365 	return array(values.map!(v => 1));
366 }
367 
368 const(ubyte)*[] paramValues(Value[] values)
369 {
370 	return array(values.map!(v => v.valuePointer));
371 }