OciDefineHandle.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  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 <[email protected]>
  14. // Daniel Morgan <[email protected]>
  15. //
  16. // Copyright (C) Tim Coleman, 2003
  17. // Copyright (C) Daniel Morgan, 2004
  18. //
  19. using System;
  20. using System.Data.OracleClient;
  21. using System.Runtime.InteropServices;
  22. using System.Text;
  23. namespace System.Data.OracleClient.Oci
  24. {
  25. internal sealed class OciDefineHandle : OciHandle, IDisposable
  26. {
  27. #region Fields
  28. bool disposed = false;
  29. //IntPtr handle;
  30. IntPtr value;
  31. short indicator;
  32. //OracleType type;
  33. OciDataType ociType;
  34. OciDataType definedType;
  35. int definedSize;
  36. short rlenp = 0;
  37. //short precision;
  38. short scale;
  39. Type fieldType;
  40. //string name;
  41. // Oracle defines the LONG VARCHAR have a size of 2 to the 31 power - 5
  42. // maybe this should settable via a config file for System.Data.OracleClient.dll
  43. // see DefineLong. Or if the a truncate sql error occurs, then retry?
  44. // or maybe allocate the memory yourself and then let oracle return error more data,
  45. // then try to get some more data
  46. internal static int LongVarCharMaxValue = (int) Int16.MaxValue - 5;
  47. OciErrorHandle errorHandle;
  48. OciLobLocator lobLocator;
  49. OciDateTimeDescriptor dateTimeDesc;
  50. #endregion // Fields
  51. #region Constructors
  52. internal OciDefineHandle (OciHandle parent, IntPtr newHandle)
  53. : base (OciHandleType.Define, parent, newHandle)
  54. {
  55. }
  56. internal void DefineByPosition (int position, OracleConnection connection)
  57. {
  58. OciParameterDescriptor parameter = ((OciStatementHandle) Parent).GetParameter (position);
  59. //name = parameter.GetName ();
  60. definedType = parameter.GetDataType ();
  61. definedSize = parameter.GetDataSize ();
  62. //precision = parameter.GetPrecision ();
  63. scale = parameter.GetScale ();
  64. Define (position, connection);
  65. parameter.Dispose ();
  66. }
  67. #endregion // Constructors
  68. #region Properties
  69. internal OciDataType DataType {
  70. get { return definedType; }
  71. }
  72. internal Type FieldType {
  73. get { return fieldType; }
  74. }
  75. internal int DefinedSize {
  76. get { return definedSize; }
  77. }
  78. internal OciErrorHandle ErrorHandle {
  79. get { return errorHandle; }
  80. set { errorHandle = value; }
  81. }
  82. internal bool IsNull {
  83. get { return (indicator == -1); }
  84. }
  85. internal short Scale {
  86. get { return scale; }
  87. }
  88. internal short Size {
  89. get { return rlenp; }
  90. }
  91. internal IntPtr Value {
  92. get { return value; }
  93. }
  94. #endregion
  95. #region Methods
  96. void Define (int position, OracleConnection connection)
  97. {
  98. switch (definedType) {
  99. case OciDataType.Date:
  100. DefineDate (position, connection);
  101. return;
  102. case OciDataType.TimeStamp:
  103. DefineTimeStamp (position, connection);
  104. return;
  105. case OciDataType.Clob:
  106. case OciDataType.Blob:
  107. DefineLob (position, definedType, connection);
  108. return;
  109. case OciDataType.Raw:
  110. DefineRaw( position, connection);
  111. return;
  112. case OciDataType.RowIdDescriptor:
  113. definedSize = 10;
  114. DefineChar (position, connection);
  115. return;
  116. case OciDataType.Integer:
  117. case OciDataType.Number:
  118. case OciDataType.Float:
  119. DefineNumber (position, connection);
  120. return;
  121. case OciDataType.Long:
  122. case OciDataType.LongVarChar:
  123. DefineLongVarChar (position, connection);
  124. return;
  125. default:
  126. DefineChar (position, connection); // HANDLE ALL OTHERS AS CHAR FOR NOW
  127. return;
  128. }
  129. }
  130. void DefineTimeStamp (int position, OracleConnection connection)
  131. {
  132. definedSize = -1;
  133. ociType = OciDataType.TimeStamp;
  134. fieldType = typeof(System.DateTime);
  135. dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
  136. if (dateTimeDesc == null) {
  137. OciErrorInfo info = connection.ErrorHandle.HandleError ();
  138. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  139. }
  140. value = dateTimeDesc.Handle;
  141. dateTimeDesc.ErrorHandle = ErrorHandle;
  142. int status = 0;
  143. status = OciCalls.OCIDefineByPosPtr (Parent,
  144. out handle,
  145. ErrorHandle,
  146. position + 1,
  147. ref value,
  148. definedSize,
  149. ociType,
  150. ref indicator,
  151. ref rlenp,
  152. IntPtr.Zero,
  153. 0);
  154. definedSize = 11;
  155. if (status != 0) {
  156. OciErrorInfo info = connection.ErrorHandle.HandleError ();
  157. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  158. }
  159. }
  160. void DefineDate (int position, OracleConnection connection)
  161. {
  162. definedSize = 7;
  163. ociType = OciDataType.Date;
  164. fieldType = typeof(System.DateTime);
  165. value = OciCalls.AllocateClear (definedSize);
  166. int status = 0;
  167. status = OciCalls.OCIDefineByPos (Parent,
  168. out handle,
  169. ErrorHandle,
  170. position + 1,
  171. value,
  172. definedSize,
  173. ociType,
  174. ref indicator,
  175. ref rlenp,
  176. IntPtr.Zero,
  177. 0);
  178. if (status != 0) {
  179. OciErrorInfo info = ErrorHandle.HandleError ();
  180. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  181. }
  182. }
  183. void DefineLongVarChar (int position, OracleConnection connection)
  184. {
  185. fieldType = typeof (System.String);
  186. // LONG VARCHAR max length is 2 to the 31 power - 5
  187. // the first 4 bytes of a LONG VARCHAR value contains the length
  188. // Int32.MaxValue - 5 causes out of memory in mono on win32
  189. // because I do not have 2GB of memory available
  190. // so Int16.MaxValue - 5 is used instead.
  191. // LAMESPEC for Oracle OCI - you can not get the length of the LONG VARCHAR value
  192. // until after you get the value. This could be why Oracle deprecated LONG VARCHAR.
  193. // If you specify a definedSize less then the length of the column value,
  194. // then you will get an OCI_ERROR ORA-01406: fetched column value was truncated
  195. definedSize = LongVarCharMaxValue;
  196. value = OciCalls.AllocateClear (definedSize);
  197. ociType = OciDataType.LongVarChar;
  198. int status = 0;
  199. status = OciCalls.OCIDefineByPos (Parent,
  200. out handle,
  201. ErrorHandle,
  202. position + 1,
  203. value,
  204. definedSize,
  205. ociType,
  206. ref indicator,
  207. ref rlenp,
  208. IntPtr.Zero, 0);
  209. rlenp = (short) definedSize;
  210. if (status != 0) {
  211. OciErrorInfo info = ErrorHandle.HandleError ();
  212. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  213. }
  214. }
  215. void DefineChar (int position, OracleConnection connection)
  216. {
  217. fieldType = typeof (System.String);
  218. int maxByteCount = Encoding.UTF8.GetMaxByteCount (definedSize);
  219. value = OciCalls.AllocateClear (maxByteCount);
  220. ociType = OciDataType.Char;
  221. int status = 0;
  222. status = OciCalls.OCIDefineByPos (Parent,
  223. out handle,
  224. ErrorHandle,
  225. position + 1,
  226. value,
  227. maxByteCount,
  228. ociType,
  229. ref indicator,
  230. ref rlenp,
  231. IntPtr.Zero,
  232. 0);
  233. OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
  234. }
  235. void DefineNumber (int position, OracleConnection connection)
  236. {
  237. fieldType = typeof (System.Decimal);
  238. value = OciCalls.AllocateClear (definedSize);
  239. ociType = OciDataType.Char;
  240. int status = 0;
  241. status = OciCalls.OCIDefineByPos (Parent,
  242. out handle,
  243. ErrorHandle,
  244. position + 1,
  245. value,
  246. definedSize * 2,
  247. ociType,
  248. ref indicator,
  249. ref rlenp,
  250. IntPtr.Zero,
  251. 0);
  252. if (status != 0) {
  253. OciErrorInfo info = ErrorHandle.HandleError ();
  254. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  255. }
  256. }
  257. void DefineLob (int position, OciDataType type, OracleConnection connection)
  258. {
  259. ociType = type;
  260. if (ociType == OciDataType.Clob)
  261. fieldType = typeof(System.String);
  262. else if (ociType == OciDataType.Blob)
  263. fieldType = typeof(byte[]);
  264. int status = 0;
  265. definedSize = -1;
  266. lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
  267. if (lobLocator == null) {
  268. OciErrorInfo info = connection.ErrorHandle.HandleError ();
  269. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  270. }
  271. value = lobLocator.Handle;
  272. lobLocator.ErrorHandle = connection.ErrorHandle;
  273. lobLocator.Service = connection.ServiceContext;
  274. status = OciCalls.OCIDefineByPosPtr (Parent,
  275. out handle,
  276. ErrorHandle,
  277. position + 1,
  278. ref value,
  279. definedSize,
  280. ociType,
  281. ref indicator,
  282. ref rlenp,
  283. IntPtr.Zero,
  284. 0);
  285. definedSize = Int32.MaxValue;
  286. if (status != 0) {
  287. OciErrorInfo info = connection.ErrorHandle.HandleError ();
  288. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  289. }
  290. }
  291. void DefineRaw (int position, OracleConnection connection)
  292. {
  293. ociType = OciDataType.Raw;
  294. fieldType = typeof (byte[]);
  295. value = OciCalls.AllocateClear (definedSize);
  296. int status = 0;
  297. status = OciCalls.OCIDefineByPos (Parent,
  298. out handle,
  299. ErrorHandle,
  300. position + 1,
  301. value,
  302. definedSize * 2,
  303. ociType,
  304. ref indicator,
  305. ref rlenp,
  306. IntPtr.Zero, 0);
  307. if (status != 0) {
  308. OciErrorInfo info = ErrorHandle.HandleError ();
  309. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  310. }
  311. }
  312. protected override void Dispose (bool disposing)
  313. {
  314. if (!disposed) {
  315. try {
  316. switch (definedType) {
  317. case OciDataType.Clob:
  318. case OciDataType.Blob:
  319. case OciDataType.TimeStamp:
  320. break;
  321. default:
  322. Marshal.FreeHGlobal (value);
  323. break;
  324. }
  325. disposed = true;
  326. } finally {
  327. base.Dispose (disposing);
  328. value = IntPtr.Zero;
  329. }
  330. }
  331. }
  332. internal OracleLob GetOracleLob ()
  333. {
  334. return new OracleLob (lobLocator, ociType);
  335. }
  336. internal object GetValue (IFormatProvider formatProvider, OracleConnection conn)
  337. {
  338. object tmp;
  339. byte [] buffer = null;
  340. switch (DataType) {
  341. case OciDataType.VarChar2:
  342. case OciDataType.String:
  343. case OciDataType.VarChar:
  344. case OciDataType.Char:
  345. case OciDataType.CharZ:
  346. case OciDataType.OciString:
  347. case OciDataType.RowIdDescriptor:
  348. buffer = new byte [Size];
  349. Marshal.Copy (Value, buffer, 0, Size);
  350. // Get length of returned string
  351. int rsize = 0;
  352. //IntPtr env = Parent.Parent; // Parent is statement, grandparent is environment
  353. IntPtr env = conn.Environment;
  354. int status = OciCalls.OCICharSetToUnicode (env, null, buffer, out rsize);
  355. OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
  356. // Get string
  357. StringBuilder ret = new StringBuilder(rsize);
  358. status = OciCalls.OCICharSetToUnicode (env, ret, buffer, out rsize);
  359. OciErrorHandle.ThrowExceptionIfError (ErrorHandle, status);
  360. return ret.ToString ();
  361. case OciDataType.LongVarChar:
  362. case OciDataType.Long:
  363. buffer = new byte [LongVarCharMaxValue];
  364. Marshal.Copy (Value, buffer, 0, buffer.Length);
  365. int longSize = 0;
  366. if (BitConverter.IsLittleEndian)
  367. longSize = BitConverter.ToInt32 (new byte[]{buffer[0], buffer[1], buffer[2], buffer[3]}, 0);
  368. else
  369. longSize = BitConverter.ToInt32 (new byte[]{buffer[3], buffer[2], buffer[1], buffer[0]}, 0);
  370. ASCIIEncoding encoding = new ASCIIEncoding ();
  371. string e = encoding.GetString (buffer, 4, longSize);
  372. return e;
  373. case OciDataType.Integer:
  374. case OciDataType.Number:
  375. case OciDataType.Float:
  376. tmp = Marshal.PtrToStringAnsi (Value, Size);
  377. if (tmp != null)
  378. return Decimal.Parse (String.Copy ((string) tmp), formatProvider);
  379. break;
  380. case OciDataType.TimeStamp:
  381. return dateTimeDesc.GetDateTime (conn.Environment, dateTimeDesc.ErrorHandle);
  382. case OciDataType.Date:
  383. return UnpackDate ();
  384. case OciDataType.Raw:
  385. byte [] raw_buffer = new byte [Size];
  386. Marshal.Copy (Value, raw_buffer, 0, Size);
  387. return raw_buffer;
  388. case OciDataType.Blob:
  389. case OciDataType.Clob:
  390. return GetOracleLob ();
  391. default:
  392. throw new Exception("OciDataType not implemented: " + DataType.ToString ());
  393. }
  394. return DBNull.Value;
  395. }
  396. internal object GetOracleValue (IFormatProvider formatProvider, OracleConnection conn)
  397. {
  398. object ovalue = GetValue (formatProvider, conn);
  399. switch (DataType) {
  400. case OciDataType.Raw:
  401. return new OracleBinary ((byte[]) ovalue);
  402. case OciDataType.Date:
  403. return new OracleDateTime ((DateTime) ovalue);
  404. case OciDataType.Blob:
  405. case OciDataType.Clob:
  406. OracleLob lob = (OracleLob) ovalue;
  407. return lob;
  408. case OciDataType.Integer:
  409. case OciDataType.Number:
  410. case OciDataType.Float:
  411. return new OracleNumber ((decimal) ovalue);
  412. case OciDataType.VarChar2:
  413. case OciDataType.String:
  414. case OciDataType.VarChar:
  415. case OciDataType.Char:
  416. case OciDataType.CharZ:
  417. case OciDataType.OciString:
  418. case OciDataType.LongVarChar:
  419. case OciDataType.Long:
  420. case OciDataType.RowIdDescriptor:
  421. return new OracleString ((string) ovalue);
  422. default:
  423. // TODO: do other types
  424. throw new NotImplementedException ();
  425. }
  426. }
  427. [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
  428. internal DateTime UnpackDate ()
  429. {
  430. byte century = Marshal.ReadByte (value, 0);
  431. byte year = Marshal.ReadByte (value, 1);
  432. byte month = Marshal.ReadByte (value, 2);
  433. byte day = Marshal.ReadByte (value, 3);
  434. byte hour = Marshal.ReadByte (value, 4);
  435. byte minute = Marshal.ReadByte (value, 5);
  436. byte second = Marshal.ReadByte (value, 6);
  437. if (hour == 0)
  438. hour ++;
  439. if (minute == 0)
  440. minute ++;
  441. if (second == 0)
  442. second ++;
  443. return new DateTime ((century - 100) * 100 + (year - 100),
  444. month,
  445. day,
  446. hour - 1,
  447. minute - 1,
  448. second - 1);
  449. }
  450. #endregion // Methods
  451. }
  452. }