1 module database.postgresql.appender;
2 
3 import std.conv;
4 import std.datetime;
5 import std.format;
6 import std.traits;
7 import std.typecons;
8 
9 import database.postgresql.protocol;
10 import database.postgresql.type;
11 
12 void appendValues(Appender, T)(ref Appender appender, T values) if (isArray!T && !isSomeString!(OriginalType!T))
13 {
14     foreach (size_t i, value; values)
15     {
16         appendValue(appender, value);
17         if (i != values.length-1)
18             appender.put(',');
19     }
20 }
21 
22 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == typeof(null)))
23 {
24     appender.put("null");
25 }
26 
27 void appendValue(Appender, T)(ref Appender appender, T value) if (isInstanceOf!(Nullable, T) || isInstanceOf!(NullableRef, T))
28 {
29     if (value.isNull)
30     {
31         appendValue(appender, null);
32     }
33     else
34     {
35         appendValue(appender, value.get);
36     }
37 }
38 
39 void appendValue(Appender, T)(ref Appender appender, T value) if (isScalarType!T)
40 {
41     static if (isBoolean!T)
42     {
43         appender.put(value ? "'t'" : "'f'");
44     }
45     else
46     {
47         appender.put(cast(ubyte[])to!string(value));
48     }
49 }
50 
51 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == SysTime))
52 {
53     value = value.toUTC;
54 
55     auto hour = value.hour;
56     auto minute = value.minute;
57     auto second = value.second;
58     auto usec = value.fracSecs.total!"usecs";
59 
60     formattedWrite(appender, "'%04d%02d%02d", value.year, value.month, value.day);
61     if (hour | minute | second | usec)
62     {
63         formattedWrite(appender, " %02d%02d%02d", hour, minute, second);
64         if (usec)
65             formattedWrite(appender, ".%06d", usec);
66     }
67     appender.put('\'');
68 }
69 
70 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == DateTime))
71 {
72     auto hour = value.hour;
73     auto minute = value.minute;
74     auto second = value.second;
75 
76     if (hour | minute | second)
77     {
78         formattedWrite(appender, "'%04d%02d%02d%02d%02d%02d'", value.year, value.month, value.day, hour, minute, second);
79     }
80     else
81     {
82         formattedWrite(appender, "'%04d%02d%02d'", value.year, value.month, value.day);
83     }
84 }
85 
86 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == TimeOfDay))
87 {
88     formattedWrite(appender, "'%02d%02d%02d'", value.hour, value.minute, value.second);
89 }
90 
91 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == Date))
92 {
93     formattedWrite(appender, "'%04d%02d%02d'", value.year, value.month, value.day);
94 }
95 
96 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == PgSQLFragment))
97 {
98     appender.put(cast(char[])value.data);
99 }
100 
101 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == PgSQLRawString))
102 {
103     appender.put('\'');
104     appender.put(cast(char[])value.data);
105     appender.put('\'');
106 }
107 
108 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == PgSQLBinary))
109 {
110     appendValue(appender, value.data);
111 }
112 
113 void appendValue(Appender, T)(ref Appender appender, T value) if (is(Unqual!T == PgSQLValue))
114 {
115     final switch(value.type) with (PgColumnTypes)
116     {
117         case UNKNOWN:
118         case NULL:
119             appender.put("null");
120             break;
121         case CHAR:
122             appendValue(appender, value.peek!char);
123             break;
124         case BOOL:
125             appendValue(appender, value.peek!bool);
126             break;
127         case INT2:
128             appendValue(appender, value.peek!short);
129             break;
130         case INT4:
131             appendValue(appender, value.peek!int);
132             break;
133         case INT8:
134             appendValue(appender, value.peek!long);
135             break;
136         case REAL:
137             appendValue(appender, value.peek!float);
138             break;
139         case DOUBLE:
140             appendValue(appender, value.peek!double);
141             break;
142         case POINT:
143         case LSEG:
144         case PATH:
145         case BOX:
146         case POLYGON:
147         case LINE:
148         case TINTERVAL:
149         case INTERVAL:
150         case CIRCLE:
151         case BYTEA:
152         case JSONB:
153             appendValue(appender, value.peek!(ubyte[]));
154             break;
155         case NUMERIC:
156         case MONEY:
157         case BIT:
158         case VARBIT:
159         case INET:
160         case CIDR:
161         case MACADDR:
162         case MACADDR8:
163         case UUID:
164         case JSON:
165         case XML:
166         case TEXT:
167         case NAME:
168         case VARCHAR:
169         case CHARA:
170             appendValue(appender, value.peek!(char[]));
171             break;
172         case DATE:
173             appendValue(appender, value.peek!Date);
174             break;
175         case TIMETZ:
176         case TIME:
177             appendValue(appender, value.peek!TimeOfDay);
178             break;
179         case TIMESTAMP:
180             appendValue(appender, value.peek!DateTime);
181             break;
182         case TIMESTAMPTZ:
183             appendValue(appender, value.peek!SysTime);
184             break;
185     }
186 }
187 
188 void appendValue(Appender, T)(ref Appender appender, T value) if (isArray!T && (is(Unqual!(typeof(T.init[0])) == ubyte) || is(Unqual!(typeof(T.init[0])) == char)))
189 {
190     appender.put('\'');
191     auto ptr = value.ptr;
192     auto end = value.ptr + value.length;
193     while (ptr != end)
194     {
195         switch(*ptr)
196         {
197         case '\\':
198         case '\'':
199             appender.put('\\');
200             goto default;
201         default:
202             appender.put(*ptr++);
203         }
204     }
205     appender.put('\'');
206 }