1 // NpgsqlTypes.NpgsqlTypesHelper.cs
4 // Glen Parker <glenebob@nwlink.com>
6 // Copyright (C) 2004 The Npgsql Development Team
7 // npgsql-general@gborg.postgresql.org
8 // http://gborg.postgresql.org/project/npgsql/projdisplay.php
10 // This library is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU Lesser General Public
12 // License as published by the Free Software Foundation; either
13 // version 2.1 of the License, or (at your option) any later version.
15 // This library is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 // Lesser General Public License for more details.
20 // You should have received a copy of the GNU Lesser General Public
21 // License along with this library; if not, write to the Free Software
22 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 // This file provides data type converters between PostgreSQL representations
28 using System.Collections;
29 using System.Globalization;
32 using System.Text.RegularExpressions;
39 /// Provide event handlers to convert all native supported basic data types from their backend
40 /// text representation to a .NET object.
42 internal abstract class BasicBackendToNativeTypeConverter
44 private static readonly String[] DateFormats = new String[]
49 private static readonly String[] TimeFormats = new String[]
67 private static readonly String[] DateTimeFormats = new String[]
69 "yyyy-MM-dd HH:mm:ss.ffffff",
70 "yyyy-MM-dd HH:mm:ss.fffff",
71 "yyyy-MM-dd HH:mm:ss.ffff",
72 "yyyy-MM-dd HH:mm:ss.fff",
73 "yyyy-MM-dd HH:mm:ss.ff",
74 "yyyy-MM-dd HH:mm:ss.f",
75 "yyyy-MM-dd HH:mm:ss",
76 "yyyy-MM-dd HH:mm:ss.ffffffzz",
77 "yyyy-MM-dd HH:mm:ss.fffffzz",
78 "yyyy-MM-dd HH:mm:ss.ffffzz",
79 "yyyy-MM-dd HH:mm:ss.fffzz",
80 "yyyy-MM-dd HH:mm:ss.ffzz",
81 "yyyy-MM-dd HH:mm:ss.fzz",
82 "yyyy-MM-dd HH:mm:sszz"
88 internal static Object ToBinary(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
91 Int32 byteAPosition = 0;
92 Int32 byteAStringLength = BackendData.Length;
93 MemoryStream ms = new MemoryStream();
95 while (byteAPosition < byteAStringLength)
97 // The IsDigit is necessary in case we receive a \ as the octal value and not
98 // as the indicator of a following octal value in decimal format.
100 if (BackendData[byteAPosition] == '\\') {
102 if (byteAPosition + 1 == byteAStringLength)
107 else if (Char.IsDigit(BackendData[byteAPosition + 1]))
109 octalValue = (Byte.Parse(BackendData[byteAPosition + 1].ToString()) << 6);
110 octalValue |= (Byte.Parse(BackendData[byteAPosition + 2].ToString()) << 3);
111 octalValue |= Byte.Parse(BackendData[byteAPosition + 3].ToString());
122 octalValue = (Byte)BackendData[byteAPosition];
127 ms.WriteByte((Byte)octalValue);
135 /// Convert a postgresql boolean to a System.Boolean.
137 internal static Object ToBoolean(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
139 return (BackendData.ToLower() == "t" ? true : false);
143 /// Convert a postgresql datetime to a System.DateTime.
145 internal static Object ToDateTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
147 // Get the date time parsed in all expected formats for timestamp.
148 return DateTime.ParseExact(BackendData,
150 DateTimeFormatInfo.InvariantInfo,
151 DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
155 /// Convert a postgresql date to a System.DateTime.
157 internal static Object ToDate(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
159 return DateTime.ParseExact(BackendData,
161 DateTimeFormatInfo.InvariantInfo,
162 DateTimeStyles.AllowWhiteSpaces);
166 /// Convert a postgresql time to a System.DateTime.
168 internal static Object ToTime(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
170 return DateTime.ParseExact(BackendData,
172 DateTimeFormatInfo.InvariantInfo,
173 DateTimeStyles.NoCurrentDateDefault | DateTimeStyles.AllowWhiteSpaces);
177 /// Convert a postgresql money to a System.Decimal.
179 internal static Object ToMoney(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
181 // It's a number with a $ on the beginning...
182 return Convert.ToDecimal(BackendData.Substring(1, BackendData.Length - 1), CultureInfo.InvariantCulture);
187 /// Provide event handlers to convert the basic native supported data types from
188 /// native form to backend representation.
190 internal abstract class BasicNativeToBackendTypeConverter
195 internal static String ToString(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
197 return NativeData.ToString().Replace("'", "''").Replace("\\", "\\\\");
203 internal static String ToBinary(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
205 Byte[] byteArray = (Byte[])NativeData;
206 int len = byteArray.Length;
207 char[] res = new char[len * 5];
209 for (int i = 0, o = 0; i < len; ++i, o += 5)
211 byte item = byteArray[i];
212 res[o] = res[o + 1] = '\\';
213 res[o + 2] = (char)('0' + (7 & (item >> 6)));
214 res[o + 3] = (char)('0' + (7 & (item >> 3)));
215 res[o + 4] = (char)('0' + (7 & item));
218 return new String(res);
222 /// Convert to a postgresql boolean.
224 internal static String ToBoolean(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
226 return ((bool)NativeData) ? "TRUE" : "FALSE";
230 /// Convert to a postgresql timestamp.
232 internal static String ToDateTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
234 return ((DateTime)NativeData).ToString("yyyy-MM-dd HH:mm:ss.fff", DateTimeFormatInfo.InvariantInfo);
238 /// Convert to a postgresql date.
240 internal static String ToDate(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
242 return ((DateTime)NativeData).ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
246 /// Convert to a postgresql time.
248 internal static String ToTime(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
250 return ((DateTime)NativeData).ToString("HH:mm:ss.fff", DateTimeFormatInfo.InvariantInfo);
254 /// Convert to a postgres money.
256 internal static String ToMoney(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
258 return "$" + ((Decimal)NativeData).ToString(DateTimeFormatInfo.InvariantInfo);
264 /// Provide event handlers to convert extended native supported data types from their backend
265 /// text representation to a .NET object.
267 internal abstract class ExtendedBackendToNativeTypeConverter
270 private static readonly Regex pointRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)");
271 private static readonly Regex boxlsegRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\),\((-?\d+.?\d*),(-?\d+.?\d*)\)");
272 private static readonly Regex pathpolygonRegex = new Regex(@"\((-?\d+.?\d*),(-?\d+.?\d*)\)");
273 private static readonly Regex circleRegex = new Regex(@"<\((-?\d+.?\d*),(-?\d+.?\d*)\),(\d+.?\d*)>");
277 /// Convert a postgresql point to a System.NpgsqlPoint.
279 internal static Object ToPoint(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
282 Match m = pointRegex.Match(BackendData);
284 return new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString()));
291 /// Convert a postgresql point to a System.RectangleF.
293 internal static Object ToBox(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
296 Match m = boxlsegRegex.Match(BackendData);
298 return new NpgsqlBox(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())),
299 new NpgsqlPoint(Single.Parse(m.Groups[3].ToString()), Single.Parse(m.Groups[4].ToString())));
305 internal static Object ToLSeg(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
307 Match m = boxlsegRegex.Match(BackendData);
309 return new NpgsqlLSeg(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())),
310 new NpgsqlPoint(Single.Parse(m.Groups[3].ToString()), Single.Parse(m.Groups[4].ToString())));
316 internal static Object ToPath(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
319 Match m = pathpolygonRegex.Match(BackendData);
320 Boolean open = (BackendData[0] == '[');
321 ArrayList points = new ArrayList();
327 points.Add(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())));
330 // Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with
331 // a trailling ')' only when the last character of the string is a ')' which is the case for closed paths
332 // returned by backend. This gives parsing exception when converting to single.
333 // I still don't know if this is a bug in mono or in my regular expression.
334 // Check if there is this character and remove it.
336 String group2 = m.Groups[2].ToString();
337 if (group2.EndsWith(")"))
338 group2 = group2.Remove(group2.Length - 1, 1);
340 points.Add(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(group2)));
347 NpgsqlPath result = new NpgsqlPath((NpgsqlPoint[]) points.ToArray(typeof(NpgsqlPoint)));
348 result.IsOpen = open;
357 internal static Object ToPolygon(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
360 Match m = pathpolygonRegex.Match(BackendData);
361 ArrayList points = new ArrayList();
366 // Here we have to do a little hack, because as of 2004-08-11 mono cvs version, the last group is returned with
367 // a trailling ')' only when the last character of the string is a ')' which is the case for closed paths
368 // returned by backend. This gives parsing exception when converting to single.
369 // I still don't know if this is a bug in mono or in my regular expression.
370 // Check if there is this character and remove it.
372 String group2 = m.Groups[2].ToString();
373 if (group2.EndsWith(")"))
374 group2 = group2.Remove(group2.Length - 1, 1);
376 points.Add(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(group2)));
383 return new NpgsqlPolygon((NpgsqlPoint[]) points.ToArray(typeof(NpgsqlPoint)));
390 internal static Object ToCircle(NpgsqlBackendTypeInfo TypeInfo, String BackendData, Int16 TypeSize, Int32 TypeModifier)
392 Match m = circleRegex.Match(BackendData);
393 return new NpgsqlCircle(new NpgsqlPoint(Single.Parse(m.Groups[1].ToString()), Single.Parse(m.Groups[2].ToString())), Single.Parse(m.Groups[3].ToString()));
399 /// Provide event handlers to convert extended native supported data types from
400 /// native form to backend representation.
402 internal abstract class ExtendedNativeToBackendTypeConverter
407 internal static String ToPoint(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
409 if (NativeData is NpgsqlPoint)
411 NpgsqlPoint P = (NpgsqlPoint)NativeData;
412 return String.Format(CultureInfo.InvariantCulture, "({0},{1})", P.X, P.Y);
416 throw new InvalidCastException("Unable to cast data to NpgsqlPoint type");
423 internal static String ToBox(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
425 /*if (NativeData.GetType() == typeof(Rectangle)) {
426 Rectangle R = (Rectangle)NativeData;
427 return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", R.Left, R.Top, R.Left + R.Width, R.Top + R.Height);
428 } else if (NativeData.GetType() == typeof(RectangleF)) {
429 RectangleF R = (RectangleF)NativeData;
430 return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", R.Left, R.Top, R.Left + R.Width, R.Top + R.Height);*/
432 if (NativeData is NpgsqlBox)
434 NpgsqlBox box = (NpgsqlBox) NativeData;
435 return String.Format(CultureInfo.InvariantCulture, "({0},{1}),({2},{3})", box.LowerLeft.X, box.LowerLeft.Y, box.UpperRight.X, box.UpperRight.Y);
438 throw new InvalidCastException("Unable to cast data to Rectangle type");
445 internal static String ToLSeg(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
447 NpgsqlLSeg S = (NpgsqlLSeg)NativeData;
448 return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2},{3}", S.Start.X, S.Start.Y, S.End.X, S.End.Y);
454 internal static String ToPath(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
456 StringBuilder B = new StringBuilder();
458 foreach (NpgsqlPoint P in ((NpgsqlPath)NativeData).Points) {
459 B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y);
462 return String.Format("[{0}]", B.ToString());
468 internal static String ToPolygon(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
470 StringBuilder B = new StringBuilder();
472 foreach (NpgsqlPoint P in ((NpgsqlPolygon)NativeData).Points) {
473 B.AppendFormat(CultureInfo.InvariantCulture, "{0}({1},{2})", (B.Length > 0 ? "," : ""), P.X, P.Y);
476 return String.Format("({0})", B.ToString());
482 internal static String ToCircle(NpgsqlNativeTypeInfo TypeInfo, Object NativeData)
484 NpgsqlCircle C = (NpgsqlCircle)NativeData;
485 return String.Format(CultureInfo.InvariantCulture, "{0},{1},{2}", C.Center.X, C.Center.Y, C.Radius);