1 module dpq.value;
2 
3 import dpq.result;
4 import dpq.exception;
5 import dpq.pgarray;
6 import dpq.meta;
7 import dpq.attributes;
8 import dpq.connection;
9 
10 //import derelict.pq.pq;
11 import libpq.libpq;
12 
13 import std.algorithm : map;
14 import std.array;
15 import std.conv : to;
16 import std.typecons : Nullable, TypedefType;
17 import std.bitmanip;
18 import std.traits;
19 import std.datetime : SysTime, DateTime;
20 
21 version(unittest) import std.stdio;
22 
23 enum POSTGRES_EPOCH = DateTime(2000, 1, 1);
24 
25 package enum Type : Oid
26 {
27 	INFER = 0,
28 	BOOL = 16,
29 	BYTEA = 17,
30 	CHAR = 18,
31 	NAME = 19,
32 	INT8 = 20,
33 	INT2 = 21,
34 	INT2VECTOR = 22,
35 	INT4 = 23,
36 	REGPROC = 24,
37 	TEXT = 25,
38 	OID = 26,
39 	TID = 27,
40 	XID = 28,
41 	CID = 29,
42 	OIDVECTOR = 30,
43 	JSON = 114,
44 	XML = 142,
45 	PGNODETREE = 194,
46 	POINT = 600,
47 	LSEG = 601,
48 	PATH = 602,
49 	BOX = 603,
50 	POLYGON = 604,
51 	LINE = 628,
52 	FLOAT4 = 700,
53 	FLOAT8 = 701,
54 	ABSTIME = 702,
55 	RELTIME = 703,
56 	TINTERVAL = 704,
57 	UNKNOWN = 705,
58 	CIRCLE = 718,
59 	CASH = 790,
60 	MACADDR = 829,
61 	INET = 869,
62 	CIDR = 650,
63 	INT2ARRAY = 1005,
64 	INT4ARRAY = 1007,
65 	TEXTARRAY = 1009,
66 	INT8ARRAY = 1016,
67 	OIDARRAY = 1028,
68 	FLOAT4ARRAY = 1021,
69 	ACLITEM = 1033,
70 	CSTRINGARRAY = 1263,
71 	BPCHAR = 1042,
72 	VARCHAR = 1043,
73 	DATE = 1082,
74 	TIME = 1083,
75 	TIMESTAMP = 1114,
76 	TIMESTAMPTZ = 1184,
77 	INTERVAL = 1186,
78 	TIMETZ = 1266,
79 	BIT = 1560,
80 	VARBIT = 1562,
81 	NUMERIC = 1700,
82 	REFCURSOR = 1790,
83 	REGPROCEDURE = 2202,
84 	REGOPER = 2203,
85 	REGOPERATOR = 2204,
86 	REGCLASS = 2205,
87 	REGTYPE = 2206,
88 	REGTYPEARRAY = 2211,
89 	UUID = 2950,
90 	LSN = 3220,
91 	TSVECTOR = 3614,
92 	GTSVECTOR = 3642,
93 	TSQUERY = 3615,
94 	REGCONFIG = 3734,
95 	REGDICTIONARY = 3769,
96 	JSONB = 3802,
97 	INT4RANGE = 3904,
98 	RECORD = 2249,
99 	RECORDARRAY = 2287,
100 	CSTRING = 2275,
101 	ANY = 2276,
102 	ANYARRAY = 2277,
103 	VOID = 2278,
104 	TRIGGER = 2279,
105 	EVTTRIGGER = 3838,
106 	LANGUAGE_HANDLER = 2280,
107 	INTERNAL = 2281,
108 	OPAQUE = 2282,
109 	ANYELEMENT = 2283,
110 	ANYNONARRAY = 2776,
111 	ANYENUM = 3500,
112 	FDW_HANDLER = 3115,
113 	ANYRANGE = 3831,
114 }
115 
116 package Oid[string] customTypes;
117 
118 struct Value
119 {
120 	private
121 	{
122 		ubyte[] _valueBytes;
123 		int _size;
124 		Type _type;
125 		bool _isNull;
126 	}
127 
128 	this(typeof(null) n)
129 	{
130 		_isNull = true;
131 	}
132 
133 	this(T)(T val)
134 	{
135 		opAssign(val);
136 	}
137 
138 	this(T)(T* val, int len, Type type = Type.INFER)
139 	{
140 		_size = len;
141 		_type = type;
142 
143 		_valueBytes = val[0 .. len].dup;
144 	}
145 
146 	void opAssign(T)(T val)
147 			if (isArray!T)
148 	{
149 		_size = (ForeachType!T.sizeof * val.length).to!int;
150 
151 		static if (is(T == ubyte[]))
152 			_valueBytes = val;
153 		else
154 		{
155 			_valueBytes = PGArray(val).toBytes();
156 			_size = _valueBytes.length.to!int;
157 		}
158 
159 		_type = typeOid!T;
160 	}
161 
162 	void opAssign(T)(T val)
163 			if(!isArray!T)
164 	{
165 		if (isAnyNull(val))
166 		{
167 			_size = 0;
168 			_valueBytes = null;
169 			_isNull = true;
170 
171 			return;
172 		}
173 
174 		_valueBytes = toBytes(val);
175 		_size = _valueBytes.length.to!int;
176 		_type = typeOid!T;
177 	}
178 
179 	void opAssign(string val)
180 	{
181 		import std.string;
182 
183 		_valueBytes = val.representation.dup;
184 		_size = _valueBytes.length.to!int;
185 		_type = Type.TEXT;
186 	}
187 
188 	void opAssign(SysTime val)
189 	{
190 		_type = typeOid!SysTime;
191 		_size = typeof(val.stdTime).sizeof;
192 
193 		_valueBytes = toBytes(val);
194 	}
195 	
196 	void opAssign(Value val)
197 	{
198 		_valueBytes = val._valueBytes;
199 		_size = val._size;
200 		_type = val._type;
201 	}
202 
203 	/**
204 		Converts the given type to an ubyte[], as PostgreSQL expects it. Ignores
205 		any Nullable specifiers and Typedefs.
206 	 */
207 	Nullable!(ubyte[]) toBytes(T)(T val)
208 	{
209 		alias NT = NoNullable!T;
210 		alias AT = TypedefType!NT;
211 
212 		if (isAnyNull(val))
213 			return Nullable!(ubyte[]).init;
214 
215 		return Nullable!(ubyte[])(toBytesImpl(cast(AT) val));
216 	}
217 
218 	private ubyte[] toBytesImpl(T)(T val)
219 			if (isScalarType!T)
220 	{
221 		return nativeToBigEndian(val).dup;
222 	}
223 	
224 	private ubyte[] toBytesImpl(T)(T val)
225 			if (isSomeString!T)
226 	{
227 		import std.string : representation;
228 		return val.representation.dup;
229 	}
230 
231 	private ubyte[] toBytesImpl(T)(T val)
232 			if (is(T == class) || is(T == struct))
233 	{
234 		alias members = serialisableMembers!T;
235 		ubyte[] bytes;
236 
237 		size_t index = 0;
238 		if (isAnyNull(val))
239 			return nativeToBigEndian(cast(int) -1).dup;
240 		else	
241 			bytes ~= nativeToBigEndian(cast(int) members.length);
242 
243 		foreach (mName; members)
244 		{
245 			auto m = __traits(getMember, val, mName);
246 			alias MT = NoNullable!(typeof(m));
247 
248 
249 			// Element's OID
250 			bytes ~= nativeToBigEndian(cast(int) oidForType!MT);
251 
252 			auto bs = toBytes(m);
253 
254 			// Null values have their length written as -1, nothing else is written
255 			if (bs.isNull)
256 				bytes ~= nativeToBigEndian(cast(int) -1);
257 			else
258 			{
259 				// Element's length in bytes
260 				bytes ~= nativeToBigEndian(bs.length.to!int);
261 
262 				// Actual element data
263 				bytes ~= bs;
264 			}
265 		}
266 
267 		return bytes;
268 	}
269 
270 	private ubyte[] toBytesImpl(SysTime val)
271 	{
272 		import core.time;
273 
274 		// stdTime is in hnsecs, psql wants microsecs
275 		long diff = val.stdTime - SysTime(POSTGRES_EPOCH).stdTime;
276 		return nativeToBigEndian(diff / 10).dup;
277 	}
278 
279 
280 
281 	unittest
282 	{
283 		import std.string;
284 
285 		writeln(" * value");
286 		writeln("\t * opAssign");
287 
288 		int a = 0xFFFF_FFFF;
289 		Value v;
290 		v.opAssign(a);
291 
292 		assert(v._size == 4);
293 		assert(v._valueBytes == [255, 255, 255, 255]);
294 		assert(v._type == Type.INT4);
295 		
296 		int[][] b = [[1], [2]];
297 		auto pga = PGArray(b);
298 
299 		v.opAssign(b);
300 		assert(v._size == pga.toBytes().length);
301 		assert(v._valueBytes == pga.toBytes());
302 
303 		string str = "some string, I don't even know.";
304 		v.opAssign(str);
305 
306 		assert(v._valueBytes == str.representation);
307 		assert(v.size == str.representation.length);
308 
309 		Value v2;
310 		v.opAssign(v2);
311 		assert(v2 == v);
312 
313 		import std.datetime;
314 		SysTime t = Clock.currTime;
315 		v2 = t;
316 
317 		assert(v2.as!SysTime == t);
318 
319 		Nullable!int ni;
320 		assert(Value(ni).as!int.isNull);
321 		ni = 5;
322 		assert(Value(ni).as!int == ni);
323 	}
324 
325 	@property int size()
326 	{
327 		return _size;
328 	}
329 
330 	@property Oid type()
331 	{
332 		return _type;
333 	}
334 	
335 	@property const(ubyte)* valuePointer()
336 	{
337 		return _valueBytes.ptr;
338 	}
339 
340 	T as(T)()
341 		if (isInstanceOf!(Nullable, T))
342 	{
343 		alias RT = ReturnType!(T.get);
344 		return as!(Unqual!RT);
345 	}
346 
347 	Nullable!T as(T)()
348 		if (!isInstanceOf!(Nullable, T))
349 	{
350 		alias RT = Unqual!T;
351 
352 		if (_isNull)
353 			return Nullable!RT.init;
354 
355 		const(ubyte)[] data = _valueBytes[0 .. _size];
356 		return fromBytes!RT(data, _size);
357 	}
358 
359 	unittest
360 	{
361 		import std.typecons : Typedef;
362 
363 		writeln("\t * as");
364 
365 		Value v = "123";
366 		assert(v.as!string == "123");
367 
368 		v = 123;
369 		assert(v.as!int == 123);
370 
371 		v = [[1, 2], [3, 4]];
372 		assert(v.as!(int[][]) == [[1, 2],[3, 4]]);
373 
374 		alias MyInt = Typedef!int;
375 		MyInt x = 2;
376 		v = Value(x);
377 		writefln("Val is %s", v);
378 		assert(v.as!MyInt == x, v.as!(MyInt).to!string ~ " and " ~ x.to!string ~ " are not equal");
379 	}
380 }
381 
382 template typeOid(T)
383 {
384 		alias TU = std.typecons.Unqual!T;
385 		static if (isArray!T && !isSomeString!T)
386 		{
387 			alias BT = BaseType!T;
388 			static if (is(BT == int))
389 				enum typeOid = Type.INT4ARRAY;
390 			else static if (is(BT == long))
391 				enum typeOid = Type.INT8ARRAY;
392 			else static if (is(BT == short))
393 				enum typeOid = Type.INT2ARRAY;
394 			else static if (is(BT == float))
395 				enum typeOid = Type.FLOAT4ARRAY;
396 			else static if (is(BT == byte) || is (BT == ubyte))
397 				enum typeOid = Type.BYTEA;
398 			else
399 				static assert(false, "Cannot map array type " ~ T.stringof ~ " to Oid");
400 		}
401 		else
402 		{
403 			static if (is(TU == int))
404 				enum typeOid = Type.INT4;
405 			else static if (is(TU == long))
406 				enum typeOid = Type.INT8;
407 			else static if (is(TU == bool))
408 				enum typeOid = Type.BOOL;
409 			else static if (is(TU == byte))
410 				enum typeOid = Type.CHAR;
411 			else static if (is(TU == char))
412 				enum typeOid = Type.CHAR;
413 			else static if (isSomeString!TU)
414 				enum typeOid = Type.TEXT;
415 			else static if (is(TU == short))
416 				enum typeOid = Type.INT2;
417 			else static if (is(TU == float))
418 				enum typeOid = Type.FLOAT4;
419 			else static if (is(TU == double))
420 				enum typeOid = Type.FLOAT8;
421 			else static if (is(TU == SysTime))
422 				enum typeOid = Type.TIMESTAMP;
423 
424 			/**
425 				Since unsigned types are not supported by PostgreSQL, we use signed
426 				types for them. Transfer and representation in D will still work correctly,
427 				but SELECTing them in the psql console, or as a string might result in 
428 				a negative number.
429 
430 				It is recommended not to use unsigned types in structures, that will
431 				be used in the DB directly.
432 			*/
433 			else static if (is(TU == ulong))
434 				enum typeOid = Type.INT8;
435 			else static if (is(TU == uint))
436 				enum typeOid = Type.INT4;
437 			else static if (is(TU == ushort) || is(TU == char))
438 				enum typeOid = Type.INT2;
439 			else static if (is(TU == ubyte))
440 				enum typeOid = Type.CHAR;
441 			else
442 				// Try to infer
443 				enum typeOid = Type.INFER;
444 		}
445 }
446 
447 // TODO: this for arrays
448 Type oidForType(T)()
449 		if (!isArray!T)
450 {
451 	enum oid = typeOid!T;
452 
453 	static if (oid == Type.INFER)
454 	{
455 		Oid* p;
456 		if ((p = relationName!T in _dpqCustomOIDs) != null)
457 			return cast(Type) *p;
458 	}
459 
460 	return oid;
461 }
462 
463 unittest
464 {
465 	writeln("\t * typeOid");
466 
467 	static assert(typeOid!int == Type.INT4, "int");
468 	static assert(typeOid!string == Type.TEXT, "string");
469 	static assert(typeOid!(int[]) == Type.INT4ARRAY, "int[]");
470 	static assert(typeOid!(int[][]) == Type.INT4ARRAY, "int[][]");
471 	static assert(typeOid!(ubyte[]) == Type.BYTEA, "ubyte[]");
472 }
473 
474 Oid[] paramTypes(Value[] values)
475 {
476 	return array(values.map!(v => v.type));
477 }
478 
479 int[] paramLengths(Value[] values)
480 {
481 	return array(values.map!(v => v.size));
482 }
483 
484 int[] paramFormats(Value[] values)
485 {
486 	return array(values.map!(v => 1));
487 }
488 
489 const(ubyte)*[] paramValues(Value[] values)
490 {
491 	return array(values.map!(v => v.valuePointer));
492 }