2005-01-06 Daniel Morgan <danielmorgan@verizon.net>
[mono.git] / mcs / class / System.Data.OracleClient / System.Data.OracleClient.Oci / OciDefineHandle.cs
1 // 
2 // OciDefineHandle.cs 
3 //  
4 // Part of managed C#/.NET library System.Data.OracleClient.dll
5 //
6 // Part of the Mono class libraries at
7 // mcs/class/System.Data.OracleClient/System.Data.OracleClient.Oci
8 //
9 // Assembly: System.Data.OracleClient.dll
10 // Namespace: System.Data.OracleClient.Oci
11 // 
12 // Authors: 
13 //     Tim Coleman <tim@timcoleman.com>
14 //     Daniel Morgan <danielmorgan@verizon.net>
15 //         
16 // Copyright (C) Tim Coleman, 2003
17 // Copyright (C) Daniel Morgan, 2004
18 // 
19
20 using System;
21 using System.Data.OracleClient;
22 using System.Runtime.InteropServices;
23 using System.Text;
24
25 namespace System.Data.OracleClient.Oci 
26 {
27         internal sealed class OciDefineHandle : OciHandle, IDisposable
28         {
29                 #region Fields
30
31                 bool disposed = false;
32
33                 IntPtr handle;
34                 IntPtr value;
35                 short indicator;
36                 OracleType type;
37                 OciDataType ociType;
38                 OciDataType definedType;
39                 int definedSize;
40                 short rlenp = 0;
41                 short precision;
42                 short scale;
43                 Type fieldType;
44                 string name;
45
46                 OciErrorHandle errorHandle;
47
48                 OciLobLocator lobLocator;
49                 byte[] date;
50         
51                 #endregion // Fields
52
53                 #region Constructors
54
55                 public OciDefineHandle (OciHandle parent, IntPtr newHandle)
56                         : base (OciHandleType.Define, parent, newHandle)
57                 {
58                 }
59
60                 public void DefineByPosition (int position)
61                 {
62                         OciParameterDescriptor parameter = ((OciStatementHandle) Parent).GetParameter (position);
63                         
64                         name = parameter.GetName ();
65                         definedType = parameter.GetDataType ();
66                         definedSize = parameter.GetDataSize ();
67                         precision = parameter.GetPrecision ();
68                         scale = parameter.GetScale ();
69
70                         Define (position);
71
72                         parameter.Dispose ();
73                 }
74
75                 #endregion // Constructors
76
77                 #region Properties
78
79                 public OciDataType DataType {
80                         get { return definedType; }
81                 }
82
83                 public Type FieldType {
84                         get { return fieldType; }
85                 }
86
87                 public int DefinedSize {
88                         get { return definedSize; }
89                 }
90
91                 public OciErrorHandle ErrorHandle {
92                         get { return errorHandle; }
93                         set { errorHandle = value; }
94                 }
95
96                 public bool IsNull {
97                         get { return (indicator == -1); }
98                 }
99
100                 public short Scale {
101                         get { return scale; }
102                 }
103
104                 public short Size {
105                         get { return rlenp; }
106                 }
107
108                 public IntPtr Value {
109                         get { return value; }
110                 }
111
112                 #endregion
113
114                 #region Methods
115
116                 void Define (int position)
117                 {
118                         switch (definedType) {
119                         case OciDataType.Date:
120                                 DefineDate (position); 
121                                 return;
122                         case OciDataType.Clob:
123                         case OciDataType.Blob:
124                                 DefineLob (position, definedType);
125                                 return;
126                         case OciDataType.Raw:
127                                 DefineRaw( position);
128                                 return;
129                         case OciDataType.RowIdDescriptor:
130                                 definedSize = 10;
131                                 DefineChar (position);
132                                 return;
133                         case OciDataType.Integer:
134                         case OciDataType.Number:
135                         case OciDataType.Float:
136                                 DefineNumber (position);
137                                 return;
138                         default:
139                                 DefineChar (position); // HANDLE ALL OTHERS AS CHAR FOR NOW
140                                 return;
141                         }
142                 }
143
144                 void DefineDate (int position)
145                 {
146                         definedSize = 7;
147                         ociType = OciDataType.Date;
148                         fieldType = typeof(System.DateTime);
149
150                         value = Marshal.AllocHGlobal (definedSize);
151
152                         int status = 0;
153
154                         status = OciCalls.OCIDefineByPos (Parent,
155                                                 out handle,
156                                                 ErrorHandle,
157                                                 position + 1,
158                                                 value,
159                                                 definedSize,
160                                                 ociType,
161                                                 ref indicator,
162                                                 ref rlenp,
163                                                 IntPtr.Zero,
164                                                 0);
165
166                         if (status != 0) {
167                                 OciErrorInfo info = ErrorHandle.HandleError ();
168                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
169                         }
170                 }
171
172                 void DefineChar (int position)
173                 {
174                         fieldType = typeof (System.String);
175
176                         // The buffer is able to contain twice the defined size
177                         // to allow usage of multibyte characters
178                         value = Marshal.AllocHGlobal (definedSize * 2);
179
180                         ociType = OciDataType.Char;
181
182                         int status = 0;
183                         
184                         status = OciCalls.OCIDefineByPos (Parent,
185                                                 out handle,
186                                                 ErrorHandle,
187                                                 position + 1,
188                                                 value,
189                                                 definedSize * 2,
190                                                 ociType,
191                                                 ref indicator,
192                                                 ref rlenp,
193                                                 IntPtr.Zero,
194                                                 0);
195
196                         if (status != 0) {
197                                 OciErrorInfo info = ErrorHandle.HandleError ();
198                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
199                         }
200                 }
201
202                 void DefineNumber (int position) 
203                 {
204                         fieldType = typeof (System.Decimal);
205                         value = Marshal.AllocHGlobal (definedSize);
206
207                         ociType = OciDataType.Char;
208
209                         int status = 0;
210                         
211                         status = OciCalls.OCIDefineByPos (Parent,
212                                 out handle,
213                                 ErrorHandle,
214                                 position + 1,
215                                 value,
216                                 definedSize * 2,
217                                 ociType,
218                                 ref indicator,
219                                 ref rlenp,
220                                 IntPtr.Zero,
221                                 0);
222
223                         if (status != 0) {
224                                 OciErrorInfo info = ErrorHandle.HandleError ();
225                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
226                         }
227                 }
228
229                 void DefineLob (int position, OciDataType type)
230                 {
231                         ociType = type;
232
233                         if (ociType == OciDataType.Clob)
234                                 fieldType = typeof(System.String);
235                         else if (ociType == OciDataType.Blob)
236                                 fieldType = Type.GetType("System.Byte[]");
237
238                         int status = 0;
239
240                         definedSize = -1;
241
242                         lobLocator = (OciLobLocator) Parent.Parent.Allocate (OciHandleType.LobLocator);
243
244                         if (lobLocator == null) {
245                                 OciErrorInfo info = ErrorHandle.HandleError ();
246                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
247                         }
248
249                         value = lobLocator.Handle;
250                         lobLocator.ErrorHandle = ErrorHandle;
251                         lobLocator.Service = ((OciStatementHandle) Parent).Service;
252
253                         status = OciCalls.OCIDefineByPosPtr (Parent,
254                                                         out handle,
255                                                         ErrorHandle,
256                                                         position + 1,
257                                                         ref value,
258                                                         definedSize,
259                                                         ociType,
260                                                         ref indicator,
261                                                         ref rlenp,
262                                                         IntPtr.Zero,
263                                                         0);
264
265                         definedSize = Int32.MaxValue;
266
267                         if (status != 0) {
268                                 OciErrorInfo info = ErrorHandle.HandleError ();
269                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
270                         }
271                 }
272
273                 void DefineRaw (int position)
274                 {
275                         ociType = OciDataType.Raw;
276                         fieldType = Type.GetType("System.Byte[]");
277
278                         value = Marshal.AllocHGlobal (definedSize);
279
280                         int status = 0;
281
282                         status = OciCalls.OCIDefineByPos (Parent,
283                                                         out handle,
284                                                         ErrorHandle,
285                                                         position + 1,
286                                                         value,
287                                                         definedSize * 2,
288                                                         ociType,
289                                                         ref indicator,
290                                                         ref rlenp,
291                                                         IntPtr.Zero, 0);
292
293                         if (status != 0) {
294                                 OciErrorInfo info = ErrorHandle.HandleError ();
295                                 throw new OracleException (info.ErrorCode, info.ErrorMessage);
296                         }
297                 }
298
299                 protected override void Dispose (bool disposing) 
300                 {
301                         if (!disposed) {
302                                 try {
303                                         switch (definedType) {
304                                         case OciDataType.Clob:
305                                         case OciDataType.Blob:
306                                                 break;
307                                         default:
308                                                 Marshal.FreeHGlobal (value);
309                                                 break;
310                                         }
311                                         disposed = true;
312                                 } finally {
313                                         base.Dispose (disposing);
314                                         value = IntPtr.Zero;
315                                 }
316                         }
317                 }
318
319                 public OracleLob GetOracleLob ()
320                 {
321                         return new OracleLob (lobLocator, ociType);
322                 }
323
324                 public object GetValue ()
325                 {
326                         object tmp;
327
328                         switch (DataType) {
329                         case OciDataType.VarChar2:
330                         case OciDataType.String:
331                         case OciDataType.VarChar:
332                         case OciDataType.Char:
333                         case OciDataType.CharZ:
334                         case OciDataType.OciString:
335                         case OciDataType.RowIdDescriptor:
336                         case OciDataType.LongVarChar:
337                                 byte [] buffer = new byte [Size];
338                                 Marshal.Copy (Value, buffer, 0, Size);
339                                 
340                                 // Get length of returned string
341                                 int     rsize = 0;
342                                 IntPtr  env = Parent.Parent;    // Parent is statement, grandparent is environment
343                                 OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
344                         
345                                 // Get string
346                                 StringBuilder ret = new StringBuilder(rsize);
347                                 OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
348         
349                                 return ret.ToString ();
350
351                         case OciDataType.Integer:
352                         case OciDataType.Number:
353                         case OciDataType.Float:
354                                 tmp = Marshal.PtrToStringAnsi (Value, Size);
355                                 if (tmp != null)
356                                         return Decimal.Parse (String.Copy ((string) tmp));
357                                 break;
358                         case OciDataType.Date:
359                                 return UnpackDate ();
360                         case OciDataType.Raw:
361                                 byte [] raw_buffer = new byte [Size];
362                                 Marshal.Copy (Value, raw_buffer, 0, Size);
363                                 return raw_buffer;
364                         case OciDataType.Blob:
365                         case OciDataType.Clob:
366                                 return GetOracleLob ();
367                         }
368
369                         return DBNull.Value;
370                 }
371
372                 internal object GetOracleValue () 
373                 {
374                         object ovalue = GetValue ();
375
376                         switch (DataType) {
377                         case OciDataType.Raw:
378                                 return new OracleBinary ((byte[]) ovalue);
379                         case OciDataType.Date:
380                                 return new OracleDateTime ((DateTime) ovalue);
381                         case OciDataType.Blob:
382                         case OciDataType.Clob:
383                                 OracleLob lob = (OracleLob) ovalue;
384                                 return lob;
385                         case OciDataType.Integer:
386                         case OciDataType.Number:
387                         case OciDataType.Float:
388                                 return new OracleNumber ((decimal) ovalue);
389                         case OciDataType.VarChar2:
390                         case OciDataType.String:
391                         case OciDataType.VarChar:
392                         case OciDataType.Char:
393                         case OciDataType.CharZ:
394                         case OciDataType.OciString:
395                         case OciDataType.LongVarChar:
396                         case OciDataType.RowIdDescriptor:
397                                 return new OracleString ((string) ovalue);
398                         default:
399                                 // TODO: do other types
400                                 throw new NotImplementedException ();
401                         }
402                 }
403
404                 [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
405                 public DateTime UnpackDate ()
406                 {
407                         byte century = Marshal.ReadByte (value, 0);
408                         byte year = Marshal.ReadByte (value, 1);
409                         byte month = Marshal.ReadByte (value, 2);
410                         byte day = Marshal.ReadByte (value, 3);
411                         byte hour = Marshal.ReadByte (value, 4);
412                         byte minute = Marshal.ReadByte (value, 5);
413                         byte second = Marshal.ReadByte (value, 6);
414
415                         return new DateTime ((century - 100) * 100 + (year - 100),
416                                                 month,
417                                                 day,
418                                                 hour - 1,
419                                                 minute - 1,
420                                                 second - 1);
421
422                 }
423
424                 #endregion // Methods
425         }
426 }