OracleParameter.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368
  1. //
  2. // OracleParameter.cs
  3. //
  4. // Part of the Mono class libraries at
  5. // mcs/class/System.Data.OracleClient/System.Data.OracleClient
  6. //
  7. // Assembly: System.Data.OracleClient.dll
  8. // Namespace: System.Data.OracleClient
  9. //
  10. // Authors:
  11. // Tim Coleman <[email protected]>
  12. // Daniel Moragn <[email protected]>
  13. // Hubert FONGARNAND <[email protected]>
  14. //
  15. // Copyright (C) Tim Coleman , 2003
  16. // Copyright (C) Daniel Morgan, 2005, 2008
  17. // Copyright (C) Hubert FONGARNAND, 2005
  18. //
  19. // Licensed under the MIT/X11 License.
  20. //
  21. using System;
  22. using System.Collections;
  23. using System.ComponentModel;
  24. using System.Data;
  25. #if NET_2_0
  26. using System.Data.Common;
  27. #endif
  28. using System.Data.SqlTypes;
  29. using System.Data.OracleClient.Oci;
  30. using System.Globalization;
  31. using System.Runtime.InteropServices;
  32. using System.Text;
  33. namespace System.Data.OracleClient
  34. {
  35. [TypeConverter (typeof(OracleParameter.OracleParameterConverter))]
  36. public sealed class OracleParameter :
  37. #if NET_2_0
  38. DbParameter, IDbDataParameter, ICloneable
  39. #else
  40. MarshalByRefObject, IDbDataParameter, IDataParameter, ICloneable
  41. #endif
  42. {
  43. #region Fields
  44. string name;
  45. OracleType oracleType = OracleType.VarChar;
  46. OciDataType ociType;
  47. int size;
  48. ParameterDirection direction = ParameterDirection.Input;
  49. bool isNullable;
  50. byte precision;
  51. byte scale;
  52. string srcColumn;
  53. #if NET_2_0
  54. bool sourceColumnNullMapping;
  55. #endif
  56. DataRowVersion srcVersion;
  57. DbType dbType = DbType.AnsiString;
  58. int offset;
  59. bool sizeSet;
  60. bool oracleTypeSet;
  61. object value = DBNull.Value;
  62. OciLobLocator lobLocator; // only if Blob or Clob
  63. IntPtr bindOutValue = IntPtr.Zero;
  64. OciDateTimeDescriptor dateTimeDesc;
  65. IntPtr cursor = IntPtr.Zero;
  66. OracleParameterCollection container;
  67. OciBindHandle bindHandle;
  68. OracleConnection connection;
  69. byte[] bytes;
  70. IntPtr bindValue = IntPtr.Zero;
  71. bool useRef;
  72. OciDataType bindType;
  73. OracleType bindOracleType;
  74. short indicator;
  75. int bindSize;
  76. #endregion // Fields
  77. #region Constructors
  78. // constructor for cloning the object
  79. private OracleParameter (OracleParameter value)
  80. {
  81. this.name = value.name;
  82. this.oracleType = value.oracleType;
  83. this.ociType = value.ociType;
  84. this.size = value.size;
  85. this.direction = value.direction;
  86. this.isNullable = value.isNullable;
  87. this.precision = value.precision;
  88. this.scale = value.scale;
  89. this.srcColumn = value.srcColumn;
  90. this.srcVersion = value.srcVersion;
  91. this.dbType = value.dbType;
  92. this.offset = value.offset;
  93. this.sizeSet = value.sizeSet;
  94. this.value = value.value;
  95. this.lobLocator = value.lobLocator;
  96. this.oracleTypeSet = value.oracleTypeSet;
  97. }
  98. public OracleParameter ()
  99. {
  100. this.name = String.Empty;
  101. this.oracleType = OracleType.VarChar;
  102. this.size = 0;
  103. this.direction = ParameterDirection.Input;
  104. this.isNullable = false;
  105. this.precision = 0;
  106. this.scale = 0;
  107. this.srcColumn = String.Empty;
  108. this.srcVersion = DataRowVersion.Current;
  109. this.value = null;
  110. this.oracleTypeSet = false;
  111. }
  112. public OracleParameter (string name, object value)
  113. {
  114. this.name = name;
  115. this.value = value;
  116. if (value != null && value != DBNull.Value) {
  117. this.sizeSet = true;
  118. this.size = InferSize ();
  119. }
  120. srcColumn = string.Empty;
  121. SourceVersion = DataRowVersion.Current;
  122. InferOracleType (value);
  123. }
  124. public OracleParameter (string name, OracleType oracleType)
  125. : this (name, oracleType, 0, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
  126. {
  127. }
  128. public OracleParameter (string name, OracleType oracleType, int size)
  129. : this (name, oracleType, size, ParameterDirection.Input, false, 0, 0, String.Empty, DataRowVersion.Current, null)
  130. {
  131. }
  132. public OracleParameter (string name, OracleType oracleType, int size, string srcColumn)
  133. : this (name, oracleType, size, ParameterDirection.Input, false, 0, 0, srcColumn, DataRowVersion.Current, null)
  134. {
  135. }
  136. #if NET_2_0
  137. public OracleParameter (string name, OracleType oracleType, int size, ParameterDirection direction, string sourceColumn, DataRowVersion sourceVersion, bool sourceColumnNullMapping, object value)
  138. {
  139. this.name = name;
  140. if (size < 0)
  141. throw new ArgumentException("Size must be not be negative.");
  142. this.value = value;
  143. this.size = size;
  144. // set sizeSet to true iff value is not-null or non-zero size value
  145. if (value != null && value != DBNull.Value && size > 0)
  146. this.sizeSet = true;
  147. SourceColumnNullMapping = sourceColumnNullMapping;
  148. OracleType = oracleType;
  149. Direction = direction;
  150. SourceColumn = sourceColumn;
  151. SourceVersion = sourceVersion;
  152. }
  153. #endif
  154. public OracleParameter (string name, OracleType oracleType, int size, ParameterDirection direction, bool isNullable, byte precision, byte scale, string srcColumn, DataRowVersion srcVersion, object value)
  155. {
  156. this.name = name;
  157. if (size < 0)
  158. throw new ArgumentException("Size must be not be negative.");
  159. this.value = value;
  160. this.size = size;
  161. // set sizeSet to true iff value is not-null or non-zero size value
  162. if (value != null && value != DBNull.Value && size > 0)
  163. this.sizeSet = true;
  164. this.isNullable = isNullable;
  165. this.precision = precision;
  166. this.scale = scale;
  167. OracleType = oracleType;
  168. Direction = direction;
  169. SourceColumn = srcColumn;
  170. SourceVersion = srcVersion;
  171. }
  172. #endregion // Constructors
  173. #region Properties
  174. internal OracleParameterCollection Container {
  175. get { return container; }
  176. set { container = value; }
  177. }
  178. #if !NET_2_0
  179. [Browsable (false)]
  180. [RefreshProperties (RefreshProperties.All)]
  181. [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
  182. #endif
  183. public
  184. #if NET_2_0
  185. override
  186. #endif
  187. DbType DbType {
  188. get { return dbType; }
  189. set { SetDbType (value); }
  190. }
  191. #if !NET_2_0
  192. [DefaultValue (ParameterDirection.Input)]
  193. #endif
  194. [RefreshProperties (RefreshProperties.All)]
  195. public
  196. #if NET_2_0
  197. override
  198. #endif
  199. ParameterDirection Direction {
  200. get { return direction; }
  201. set { direction = value; }
  202. }
  203. #if !NET_2_0
  204. [Browsable (false)]
  205. [DesignOnly (true)]
  206. [DefaultValue (false)]
  207. [EditorBrowsable (EditorBrowsableState.Never)]
  208. #endif
  209. public
  210. #if NET_2_0
  211. override
  212. #endif
  213. bool IsNullable {
  214. get { return isNullable; }
  215. set { isNullable = value; }
  216. }
  217. #if NET_2_0
  218. [EditorBrowsable (EditorBrowsableState.Advanced)]
  219. #else
  220. [DefaultValue (0)]
  221. #endif
  222. [Browsable (false)]
  223. public int Offset {
  224. get { return offset; }
  225. set { offset = value; }
  226. }
  227. [DefaultValue (OracleType.VarChar)]
  228. [RefreshProperties (RefreshProperties.All)]
  229. #if NET_2_0
  230. [DbProviderSpecificTypeProperty (true)]
  231. #endif
  232. public OracleType OracleType {
  233. get { return oracleType; }
  234. set {
  235. oracleTypeSet = true;
  236. SetOracleType (value, false);
  237. }
  238. }
  239. #if !NET_2_0
  240. [DefaultValue ("")]
  241. #endif
  242. public
  243. #if NET_2_0
  244. override
  245. #endif
  246. string ParameterName {
  247. get {
  248. if (name == null)
  249. return string.Empty;
  250. return name;
  251. }
  252. set { name = value; }
  253. }
  254. #if NET_2_0
  255. [Browsable (false)]
  256. [EditorBrowsable (EditorBrowsableState.Never)]
  257. [Obsolete("Set the precision of a decimal use the Math classes.")]
  258. #else
  259. [DefaultValue (0)]
  260. #endif
  261. public byte Precision {
  262. get { return precision; }
  263. set { /* NO EFFECT*/ }
  264. }
  265. #if NET_2_0
  266. [Browsable (false)]
  267. [EditorBrowsable (EditorBrowsableState.Never)]
  268. [Obsolete("Set the precision of a decimal use the Math classes.")]
  269. #else
  270. [DefaultValue (0)]
  271. #endif
  272. public byte Scale {
  273. get { return scale; }
  274. set { /* NO EFFECT*/ }
  275. }
  276. #if !NET_2_0
  277. [DefaultValue (0)]
  278. #endif
  279. public
  280. #if NET_2_0
  281. override
  282. #endif
  283. int Size {
  284. get { return size; }
  285. set {
  286. sizeSet = true;
  287. size = value;
  288. }
  289. }
  290. #if !NET_2_0
  291. [DefaultValue ("")]
  292. #endif
  293. public
  294. #if NET_2_0
  295. override
  296. #endif
  297. string SourceColumn {
  298. get { return srcColumn; }
  299. set { srcColumn = value; }
  300. }
  301. #if NET_2_0
  302. [MonoTODO]
  303. public override bool SourceColumnNullMapping {
  304. get { return sourceColumnNullMapping; }
  305. set { sourceColumnNullMapping = value; }
  306. }
  307. #endif
  308. #if !NET_2_0
  309. [DefaultValue ("Current")]
  310. #endif
  311. public
  312. #if NET_2_0
  313. override
  314. #endif
  315. DataRowVersion SourceVersion {
  316. get { return srcVersion; }
  317. set { srcVersion = value; }
  318. }
  319. #if !NET_2_0
  320. [DefaultValue (null)]
  321. #endif
  322. [RefreshProperties (RefreshProperties.All)]
  323. [TypeConverter (typeof(StringConverter))]
  324. public
  325. #if NET_2_0
  326. override
  327. #endif
  328. object Value {
  329. get { return this.value; }
  330. set {
  331. this.value = value;
  332. if (!oracleTypeSet)
  333. InferOracleType (value);
  334. if (value != null && value != DBNull.Value) {
  335. this.size = InferSize ();
  336. this.sizeSet = true;
  337. }
  338. }
  339. }
  340. #endregion // Properties
  341. #region Methods
  342. private void AssertSizeIsSet ()
  343. {
  344. switch (ociType) {
  345. case OciDataType.VarChar2:
  346. case OciDataType.String:
  347. case OciDataType.VarChar:
  348. case OciDataType.Char:
  349. case OciDataType.CharZ:
  350. case OciDataType.OciString:
  351. if (!sizeSet)
  352. throw new Exception ("Size must be set.");
  353. break;
  354. default:
  355. break;
  356. }
  357. }
  358. internal void Bind (OciStatementHandle statement, OracleConnection con, uint pos)
  359. {
  360. connection = con;
  361. if (bindHandle == null)
  362. bindHandle = new OciBindHandle ((OciHandle) statement);
  363. IntPtr tmpHandle = bindHandle.Handle;
  364. if (Direction != ParameterDirection.Input)
  365. AssertSizeIsSet ();
  366. if (!sizeSet)
  367. size = InferSize ();
  368. bindSize = size;
  369. object v = value;
  370. int status = 0;
  371. bindType = ociType;
  372. int rsize = 0;
  373. string svalue;
  374. string sDate;
  375. DateTime dt;
  376. bool isnull = false;
  377. if (direction == ParameterDirection.Input || direction == ParameterDirection.InputOutput) {
  378. if (v == null)
  379. isnull = true;
  380. else if (v is DBNull)
  381. isnull = true;
  382. else {
  383. INullable mynullable = v as INullable;
  384. if (mynullable != null)
  385. isnull = mynullable.IsNull;
  386. }
  387. }
  388. if (isnull == true && direction == ParameterDirection.Input) {
  389. indicator = 0;
  390. bindType = OciDataType.VarChar2;
  391. bindSize = 0;
  392. } else {
  393. switch(ociType) {
  394. case OciDataType.VarChar2:
  395. case OciDataType.String:
  396. case OciDataType.VarChar:
  397. case OciDataType.Char:
  398. case OciDataType.CharZ:
  399. case OciDataType.OciString:
  400. bindType = OciDataType.String;
  401. indicator = 0;
  402. svalue = "\0";
  403. // convert value from managed type to type to marshal
  404. if (direction == ParameterDirection.Input ||
  405. direction == ParameterDirection.InputOutput) {
  406. svalue = v.ToString ();
  407. if (direction == ParameterDirection.Input && size > 0 && svalue.Length > size)
  408. svalue = svalue.Substring(0, size);
  409. svalue = svalue.ToString () + '\0';
  410. }
  411. // set bind length to size of data
  412. //bindSize = (size + 1) * 4;
  413. if (direction == ParameterDirection.Input)
  414. bindSize = Encoding.UTF8.GetMaxByteCount (svalue.Length);
  415. else
  416. bindSize = Encoding.UTF8.GetMaxByteCount (size + 1);
  417. // allocate memory based on bind length
  418. bytes = new byte [bindSize];
  419. if (direction == ParameterDirection.Input ||
  420. direction == ParameterDirection.InputOutput) {
  421. // convert managed type to memory allocated earlier
  422. // in this case using OCIUnicodeToCharSet
  423. rsize = 0;
  424. // Get size of buffer
  425. status = OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
  426. // Fill buffer
  427. status = OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
  428. }
  429. break;
  430. case OciDataType.Date:
  431. bindType = OciDataType.Date;
  432. bindSize = 7;
  433. // convert value from managed type to type to marshal
  434. if (direction == ParameterDirection.Input ||
  435. direction == ParameterDirection.InputOutput) {
  436. if (isnull)
  437. bytes = new byte [7];
  438. else {
  439. sDate = "";
  440. dt = DateTime.MinValue;
  441. if (v is String) {
  442. sDate = (string) v;
  443. dt = DateTime.Parse (sDate);
  444. }
  445. else if (v is DateTime)
  446. dt = (DateTime) v;
  447. else if (v is OracleString) {
  448. sDate = v.ToString ();
  449. dt = DateTime.Parse (sDate);
  450. }
  451. else if (v is OracleDateTime) {
  452. OracleDateTime odt = (OracleDateTime) v;
  453. dt = (DateTime) odt.Value;
  454. }
  455. else
  456. throw new NotImplementedException ("For OracleType.DateTime, data type not implemented: " + v.GetType().ToString() + ".");
  457. // for Input and InputOuput, create byte array and pack DateTime into it
  458. bytes = PackDate (dt);
  459. }
  460. } else {
  461. // allocate 7-byte array for Output and ReturnValue to put date
  462. bytes = new byte [7];
  463. }
  464. break;
  465. case OciDataType.TimeStamp:
  466. dateTimeDesc = (OciDateTimeDescriptor) connection.Environment.Allocate (OciHandleType.TimeStamp);
  467. if (dateTimeDesc == null) {
  468. OciErrorInfo info = connection.ErrorHandle.HandleError ();
  469. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  470. }
  471. dateTimeDesc.ErrorHandle = connection.ErrorHandle;
  472. bindSize = 11;
  473. bindType = OciDataType.TimeStamp;
  474. bindOutValue = dateTimeDesc.Handle;
  475. bindValue = dateTimeDesc.Handle;
  476. useRef = true;
  477. if (direction == ParameterDirection.Input ||
  478. direction == ParameterDirection.InputOutput) {
  479. dt = DateTime.MinValue;
  480. sDate = "";
  481. if (isnull)
  482. indicator = -1;
  483. else if (v is String) {
  484. sDate = (string) v;
  485. dt = DateTime.Parse (sDate);
  486. }
  487. else if (v is DateTime)
  488. dt = (DateTime) v;
  489. else if (v is OracleString) {
  490. sDate = (string) v;
  491. dt = DateTime.Parse (sDate);
  492. }
  493. else if (v is OracleDateTime) {
  494. OracleDateTime odt = (OracleDateTime) v;
  495. dt = (DateTime) odt.Value;
  496. }
  497. else
  498. throw new NotImplementedException ("For OracleType.Timestamp, data type not implemented: " + v.GetType().ToString()); // ?
  499. short year = (short) dt.Year;
  500. byte month = (byte) dt.Month;
  501. byte day = (byte) dt.Day;
  502. byte hour = (byte) dt.Hour;
  503. byte min = (byte) dt.Minute;
  504. byte sec = (byte) dt.Second;
  505. uint fsec = (uint) dt.Millisecond;
  506. string timezone = "";
  507. dateTimeDesc.SetDateTime (connection.Session,
  508. connection.ErrorHandle,
  509. year, month, day, hour, min, sec, fsec,
  510. timezone);
  511. }
  512. break;
  513. case OciDataType.Integer:
  514. case OciDataType.Float:
  515. case OciDataType.Number:
  516. bindType = OciDataType.String;
  517. indicator = 0;
  518. svalue = "\0";
  519. // define size for binding
  520. bindSize = 30; // a NUMBER is 22 bytes but as a string we need more
  521. // allocate memory
  522. bytes = new byte [bindSize];
  523. // convert value from managed type to type to marshal
  524. if (direction == ParameterDirection.Input ||
  525. direction == ParameterDirection.InputOutput) {
  526. svalue = null;
  527. if(v is IFormattable)
  528. svalue = ((IFormattable)v).ToString (null, con.SessionFormatProvider);
  529. else if (v is OracleNumber)
  530. svalue = ((OracleNumber)v).ToString(con.SessionFormatProvider);
  531. else
  532. svalue = v.ToString();
  533. svalue = svalue + "\0";
  534. rsize = 0;
  535. // Get size of buffer
  536. OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
  537. // Fill buffer
  538. bytes = new byte [bindSize];
  539. OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
  540. }
  541. break;
  542. case OciDataType.Long:
  543. case OciDataType.LongVarChar:
  544. bindType = OciDataType.LongVarChar;
  545. // FIXME: use piecewise fetching for Long, Clob, Blob, and Long Raw
  546. // See http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14250/oci05bnd.htm#sthref724
  547. bindSize = Size + 5; // 4 bytes prepended for length, bytes, 1 byte NUL character
  548. indicator = 0;
  549. svalue = "\0";
  550. // convert value from managed type to type to marshal
  551. if (direction == ParameterDirection.Input ||
  552. direction == ParameterDirection.InputOutput) {
  553. svalue = v.ToString () + '\0';
  554. }
  555. bytes = new byte [bindSize];
  556. // LONG is only ANSI
  557. ASCIIEncoding enc = new ASCIIEncoding ();
  558. if (direction == ParameterDirection.Input ||
  559. direction == ParameterDirection.InputOutput) {
  560. int byteCount = 0;
  561. if (svalue.Length > 0) {
  562. byteCount = enc.GetBytes (svalue, 4, svalue.Length, bytes, 0);
  563. // LONG VARCHAR prepends a 4-byte length
  564. if (byteCount > 0) {
  565. byte[] byteArrayLen = BitConverter.GetBytes ((uint) byteCount);
  566. bytes[0] = byteArrayLen[0];
  567. bytes[1] = byteArrayLen[1];
  568. bytes[2] = byteArrayLen[2];
  569. bytes[3] = byteArrayLen[3];
  570. }
  571. }
  572. }
  573. break;
  574. case OciDataType.Clob:
  575. if (direction == ParameterDirection.Input) {
  576. svalue = v.ToString();
  577. rsize = 0;
  578. // Get size of buffer
  579. OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
  580. // Fill buffer
  581. bytes = new byte[rsize];
  582. OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
  583. bindType = OciDataType.Long;
  584. bindSize = bytes.Length;
  585. }
  586. else if (direction == ParameterDirection.InputOutput) {
  587. // not the exact error that .net 2.0 throws, but this is better
  588. throw new NotImplementedException ("Parameters of OracleType.Clob with direction of InputOutput are not supported.");
  589. }
  590. else {
  591. // Output and Return parameters
  592. bindSize = -1;
  593. lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
  594. if (lobLocator == null) {
  595. OciErrorInfo info = connection.ErrorHandle.HandleError ();
  596. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  597. }
  598. bindOutValue = lobLocator.Handle;
  599. bindValue = lobLocator.Handle;
  600. lobLocator.ErrorHandle = connection.ErrorHandle;
  601. lobLocator.Service = statement.Service;
  602. useRef = true;
  603. }
  604. break;
  605. case OciDataType.Blob:
  606. if (direction == ParameterDirection.Input) {
  607. if (v is byte[]) {
  608. bytes = (byte[]) v;
  609. bindType = OciDataType.LongRaw;
  610. bindSize = bytes.Length;
  611. }
  612. else if (v is OracleLob) {
  613. OracleLob lob = (OracleLob) v;
  614. if (lob.LobType == OracleType.Blob) {
  615. lobLocator = lob.Locator;
  616. bindOutValue = lobLocator.Handle;
  617. bindValue = lobLocator.Handle;
  618. lobLocator.ErrorHandle = connection.ErrorHandle;
  619. lobLocator.Service = connection.ServiceContext;
  620. useRef = true;
  621. }
  622. else
  623. throw new NotImplementedException("For OracleType.Blob, data type OracleLob of LobType Clob/NClob is not implemented.");
  624. }
  625. else
  626. throw new NotImplementedException ("For OracleType.Blob, data type not implemented: " + v.GetType().ToString()); // ?
  627. }
  628. else if (direction == ParameterDirection.InputOutput) {
  629. // not the exact error that .net 2.0 throws, but this is better
  630. throw new NotImplementedException ("Parameters of OracleType.Blob with direction of InputOutput are not supported.");
  631. }
  632. else {
  633. bindSize = -1;
  634. if (value != null && value is OracleLob) {
  635. OracleLob blob = (OracleLob) value;
  636. if (blob.LobType == OracleType.Blob)
  637. if (value != OracleLob.Null) {
  638. lobLocator = blob.Locator;
  639. byte[] bs = (byte[]) blob.Value;
  640. bindSize = bs.Length;
  641. }
  642. }
  643. if (lobLocator == null) {
  644. lobLocator = (OciLobLocator) connection.Environment.Allocate (OciHandleType.LobLocator);
  645. if (lobLocator == null) {
  646. OciErrorInfo info = connection.ErrorHandle.HandleError ();
  647. throw new OracleException (info.ErrorCode, info.ErrorMessage);
  648. }
  649. }
  650. bindOutValue = lobLocator.Handle;
  651. bindValue = lobLocator.Handle;
  652. lobLocator.ErrorHandle = connection.ErrorHandle;
  653. lobLocator.Service = connection.ServiceContext;
  654. useRef = true;
  655. }
  656. break;
  657. default:
  658. // FIXME: move this up - see how Char, Number, and Date are done...
  659. if (direction == ParameterDirection.Output ||
  660. direction == ParameterDirection.InputOutput ||
  661. direction == ParameterDirection.ReturnValue) {
  662. switch(ociType) {
  663. case OciDataType.RowIdDescriptor:
  664. size = 10;
  665. bindType = OciDataType.Char;
  666. bindSize = size * 2;
  667. bindOutValue = OciCalls.AllocateClear (bindSize);
  668. bindValue = bindOutValue;
  669. break;
  670. case OciDataType.RSet: // REF CURSOR
  671. cursor = IntPtr.Zero;
  672. OciCalls.OCIHandleAlloc (connection.Environment,
  673. out cursor,
  674. OciHandleType.Statement,
  675. 0,
  676. IntPtr.Zero);
  677. bindSize = 0;
  678. bindType = OciDataType.RSet;
  679. break;
  680. default:
  681. // define other types
  682. throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
  683. } // switch of ociDataType for output
  684. bindValue = bindOutValue;
  685. }
  686. else if ((v == DBNull.Value || v == null || isnull == true) && direction == ParameterDirection.Input) {
  687. indicator = 0;
  688. bindType = OciDataType.VarChar2;
  689. bindSize = 0;
  690. }
  691. else {
  692. if (bindOracleType == OracleType.Raw) {
  693. byte[] val = v as byte[];
  694. bindValue = OciCalls.AllocateClear (val.Length);
  695. Marshal.Copy (val, 0, bindValue, val.Length);
  696. bindSize = val.Length;
  697. } else {
  698. svalue = v.ToString () + '\0';
  699. rsize = 0;
  700. // Get size of buffer
  701. OciCalls.OCIUnicodeToCharSet (statement.Parent, null, svalue, out rsize);
  702. // Fill buffer
  703. bytes = new byte[rsize];
  704. OciCalls.OCIUnicodeToCharSet (statement.Parent, bytes, svalue, out rsize);
  705. bindType = OciDataType.String;
  706. bindSize = bytes.Length;
  707. } // else oracleType
  708. } // else - Input, Ouput...
  709. break;
  710. }
  711. }
  712. // Now, call the appropriate OCI Bind function;
  713. if (useRef == true) {
  714. if (bindType == OciDataType.TimeStamp) {
  715. bindValue = dateTimeDesc.Handle;
  716. status = OciCalls.OCIBindByNameRef (statement,
  717. out tmpHandle,
  718. connection.ErrorHandle,
  719. ParameterName,
  720. ParameterName.Length,
  721. ref bindValue,
  722. bindSize,
  723. bindType,
  724. ref indicator,
  725. IntPtr.Zero,
  726. IntPtr.Zero,
  727. 0,
  728. IntPtr.Zero,
  729. 0);
  730. }
  731. else {
  732. status = OciCalls.OCIBindByNameRef (statement,
  733. out tmpHandle,
  734. connection.ErrorHandle,
  735. ParameterName,
  736. ParameterName.Length,
  737. ref bindValue,
  738. bindSize,
  739. bindType,
  740. ref indicator,
  741. IntPtr.Zero,
  742. IntPtr.Zero,
  743. 0,
  744. IntPtr.Zero,
  745. 0);
  746. }
  747. }
  748. else if (bindType == OciDataType.RSet) {
  749. status = OciCalls.OCIBindByNameRef (statement,
  750. out tmpHandle,
  751. connection.ErrorHandle,
  752. ParameterName,
  753. ParameterName.Length,
  754. ref cursor,
  755. bindSize,
  756. bindType,
  757. ref indicator,
  758. IntPtr.Zero,
  759. IntPtr.Zero,
  760. 0,
  761. IntPtr.Zero,
  762. 0);
  763. }
  764. else if (bytes != null) {
  765. status = OciCalls.OCIBindByNameBytes (statement,
  766. out tmpHandle,
  767. connection.ErrorHandle,
  768. ParameterName,
  769. ParameterName.Length,
  770. bytes,
  771. bindSize,
  772. bindType,
  773. ref indicator,
  774. IntPtr.Zero,
  775. IntPtr.Zero,
  776. 0,
  777. IntPtr.Zero,
  778. 0);
  779. }
  780. else {
  781. status = OciCalls.OCIBindByName (statement,
  782. out tmpHandle,
  783. connection.ErrorHandle,
  784. ParameterName,
  785. ParameterName.Length, // FIXME: this should be in bytes!
  786. bindValue,
  787. bindSize,
  788. bindType,
  789. ref indicator,
  790. IntPtr.Zero,
  791. IntPtr.Zero,
  792. 0,
  793. IntPtr.Zero,
  794. 0);
  795. }
  796. OciErrorHandle.ThrowExceptionIfError (connection.ErrorHandle, status);
  797. bindHandle.SetHandle (tmpHandle);
  798. }
  799. object ICloneable.Clone ()
  800. {
  801. return new OracleParameter(this);
  802. }
  803. private void InferOracleType (object value)
  804. {
  805. // Should we throw an exception here?
  806. if (value == null || value == DBNull.Value)
  807. return;
  808. Type type = value.GetType ();
  809. string exception = String.Format ("The parameter data type of {0} is invalid.", type.FullName);
  810. switch (type.FullName) {
  811. case "System.Int64":
  812. SetOracleType (OracleType.Number, true);
  813. break;
  814. case "System.Boolean":
  815. case "System.Byte":
  816. SetOracleType (OracleType.Byte, true);
  817. break;
  818. case "System.String":
  819. case "System.Data.OracleClient.OracleString":
  820. SetOracleType (OracleType.VarChar, true);
  821. break;
  822. case "System.Data.OracleClient.OracleDateTime":
  823. case "System.DateTime":
  824. SetOracleType (OracleType.DateTime, true);
  825. break;
  826. case "System.Decimal":
  827. case "System.Data.OracleClient.OracleNumber":
  828. SetOracleType (OracleType.Number, true);
  829. break;
  830. case "System.Double":
  831. SetOracleType (OracleType.Double, true);
  832. break;
  833. case "System.Byte[]":
  834. case "System.Guid":
  835. SetOracleType (OracleType.Raw, true);
  836. break;
  837. case "System.Int32":
  838. SetOracleType (OracleType.Int32, true);
  839. break;
  840. case "System.Single":
  841. SetOracleType (OracleType.Float, true);
  842. break;
  843. case "System.Int16":
  844. SetOracleType (OracleType.Int16, true);
  845. break;
  846. case "System.DBNull":
  847. break; //unable to guess type
  848. case "System.Data.OracleClient.OracleLob":
  849. SetOracleType (((OracleLob) value).LobType, true);
  850. break;
  851. default:
  852. throw new ArgumentException (exception);
  853. }
  854. }
  855. private int InferSize ()
  856. {
  857. int newSize = 0;
  858. switch (ociType) {
  859. case OciDataType.VarChar2:
  860. case OciDataType.String:
  861. case OciDataType.VarChar:
  862. case OciDataType.Char:
  863. case OciDataType.CharZ:
  864. case OciDataType.OciString:
  865. case OciDataType.Long:
  866. case OciDataType.LongVarChar:
  867. if (value == null || value == DBNull.Value)
  868. newSize = 0;
  869. else
  870. newSize = value.ToString ().Length;
  871. break;
  872. case OciDataType.RowIdDescriptor:
  873. newSize = 10;
  874. break;
  875. case OciDataType.Integer:
  876. case OciDataType.Number:
  877. case OciDataType.Float:
  878. newSize = 22;
  879. break;
  880. case OciDataType.Date:
  881. newSize = 7;
  882. break;
  883. case OciDataType.TimeStamp:
  884. newSize = 11;
  885. break;
  886. case OciDataType.Blob:
  887. case OciDataType.Clob:
  888. case OciDataType.RSet: // REF CURSOR
  889. newSize = -1;
  890. break;
  891. default:
  892. if (value == null || value == DBNull.Value)
  893. newSize = 0;
  894. else
  895. newSize = value.ToString ().Length;
  896. break;
  897. }
  898. sizeSet = true;
  899. return newSize;
  900. }
  901. private void SetDbType (DbType type)
  902. {
  903. string exception = String.Format ("No mapping exists from DbType {0} to a known OracleType.", type);
  904. switch (type) {
  905. case DbType.AnsiString:
  906. oracleType = OracleType.VarChar;
  907. ociType = OciDataType.VarChar;
  908. break;
  909. case DbType.AnsiStringFixedLength:
  910. oracleType = OracleType.Char;
  911. ociType = OciDataType.Char;
  912. break;
  913. case DbType.Binary:
  914. case DbType.Guid:
  915. oracleType = OracleType.Raw;
  916. ociType = OciDataType.Raw;
  917. break;
  918. case DbType.Boolean:
  919. case DbType.Byte:
  920. oracleType = OracleType.Byte;
  921. ociType = OciDataType.Integer;
  922. break;
  923. case DbType.Currency:
  924. case DbType.Decimal:
  925. case DbType.Int64:
  926. oracleType = OracleType.Number;
  927. ociType = OciDataType.Number;
  928. break;
  929. case DbType.Date:
  930. case DbType.DateTime:
  931. case DbType.Time:
  932. oracleType = OracleType.DateTime;
  933. ociType = OciDataType.Char;
  934. break;
  935. case DbType.Double:
  936. oracleType = OracleType.Double;
  937. ociType = OciDataType.Float;
  938. break;
  939. case DbType.Int16:
  940. oracleType = OracleType.Int16;
  941. ociType = OciDataType.Integer;
  942. break;
  943. case DbType.Int32:
  944. oracleType = OracleType.Int32;
  945. ociType = OciDataType.Integer;
  946. break;
  947. case DbType.Object:
  948. oracleType = OracleType.Blob;
  949. ociType = OciDataType.Blob;
  950. break;
  951. case DbType.Single:
  952. oracleType = OracleType.Float;
  953. ociType = OciDataType.Float;
  954. break;
  955. case DbType.String:
  956. oracleType = OracleType.NVarChar;
  957. ociType = OciDataType.VarChar;
  958. break;
  959. case DbType.StringFixedLength:
  960. oracleType = OracleType.NChar;
  961. ociType = OciDataType.Char;
  962. break;
  963. default:
  964. throw new ArgumentException (exception);
  965. }
  966. dbType = type;
  967. }
  968. private void SetOracleType (OracleType type, bool inferring)
  969. {
  970. FreeHandle ();
  971. string exception = String.Format ("No mapping exists from OracleType {0} to a known DbType.", type);
  972. switch (type) {
  973. case OracleType.BFile:
  974. case OracleType.Blob:
  975. dbType = DbType.Binary;
  976. ociType = OciDataType.Blob;
  977. break;
  978. case OracleType.LongRaw:
  979. case OracleType.Raw:
  980. dbType = DbType.Binary;
  981. ociType = OciDataType.Raw;
  982. break;
  983. case OracleType.Byte:
  984. dbType = DbType.Byte;
  985. ociType = OciDataType.Number;
  986. break;
  987. case OracleType.Char:
  988. dbType = DbType.AnsiString;
  989. ociType = OciDataType.Char;
  990. break;
  991. case OracleType.Clob:
  992. dbType = DbType.AnsiString;
  993. ociType = OciDataType.Clob;
  994. break;
  995. case OracleType.LongVarChar:
  996. case OracleType.RowId:
  997. case OracleType.VarChar:
  998. dbType = DbType.AnsiString;
  999. ociType = OciDataType.VarChar;
  1000. break;
  1001. case OracleType.Cursor: // REF CURSOR
  1002. ociType = OciDataType.RSet;
  1003. dbType = DbType.Object;
  1004. break;
  1005. case OracleType.IntervalDayToSecond:
  1006. dbType = DbType.AnsiStringFixedLength;
  1007. ociType = OciDataType.Char;
  1008. break;
  1009. case OracleType.Timestamp:
  1010. case OracleType.TimestampLocal:
  1011. case OracleType.TimestampWithTZ:
  1012. dbType = DbType.DateTime;
  1013. ociType = OciDataType.TimeStamp;
  1014. break;
  1015. case OracleType.DateTime:
  1016. dbType = DbType.DateTime;
  1017. ociType = OciDataType.Date;
  1018. break;
  1019. case OracleType.Double:
  1020. dbType = DbType.Double;
  1021. ociType = OciDataType.Number;
  1022. break;
  1023. case OracleType.Float:
  1024. dbType = DbType.Single;
  1025. ociType = OciDataType.Number;
  1026. break;
  1027. case OracleType.Int16:
  1028. dbType = DbType.Int16;
  1029. ociType = OciDataType.Number;
  1030. break;
  1031. case OracleType.Int32:
  1032. case OracleType.IntervalYearToMonth:
  1033. dbType = DbType.Int32;
  1034. ociType = OciDataType.Number;
  1035. break;
  1036. case OracleType.NChar:
  1037. dbType = DbType.StringFixedLength;
  1038. ociType = OciDataType.Char;
  1039. break;
  1040. case OracleType.NClob:
  1041. case OracleType.NVarChar:
  1042. dbType = DbType.String;
  1043. ociType = OciDataType.Char;
  1044. break;
  1045. case OracleType.Number:
  1046. dbType = DbType.VarNumeric;
  1047. ociType = OciDataType.Number;
  1048. break;
  1049. case OracleType.SByte:
  1050. dbType = DbType.SByte;
  1051. ociType = OciDataType.Number;
  1052. break;
  1053. case OracleType.UInt16:
  1054. dbType = DbType.UInt16;
  1055. ociType = OciDataType.Number;
  1056. break;
  1057. case OracleType.UInt32:
  1058. dbType = DbType.UInt32;
  1059. ociType = OciDataType.Number;
  1060. break;
  1061. default:
  1062. throw new ArgumentException (exception);
  1063. }
  1064. if (!oracleTypeSet || !inferring )
  1065. oracleType = type;
  1066. bindOracleType = type;
  1067. }
  1068. #if NET_2_0
  1069. public override void ResetDbType ()
  1070. {
  1071. ResetOracleType ();
  1072. }
  1073. public void ResetOracleType ()
  1074. {
  1075. oracleTypeSet = false;
  1076. InferOracleType (value);
  1077. }
  1078. #endif // NET_2_0
  1079. public override string ToString ()
  1080. {
  1081. return ParameterName;
  1082. }
  1083. private void GetOutValue (OracleCommand cmd)
  1084. {
  1085. // used to update the parameter value
  1086. // for Output, the output of InputOutput, and Return parameters
  1087. value = DBNull.Value;
  1088. if (indicator == -1)
  1089. return;
  1090. int rsize = 0;
  1091. IntPtr env = IntPtr.Zero;
  1092. StringBuilder ret = null;
  1093. // FIXME: redo all types - see how Char, Number, and Date are done
  1094. // here and in Bind()
  1095. switch (ociType) {
  1096. case OciDataType.VarChar2:
  1097. case OciDataType.String:
  1098. case OciDataType.VarChar:
  1099. case OciDataType.Char:
  1100. case OciDataType.CharZ:
  1101. case OciDataType.OciString:
  1102. case OciDataType.RowIdDescriptor:
  1103. // Get length of returned string
  1104. rsize = 0;
  1105. env = cmd.Connection.Environment;
  1106. OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
  1107. // Get string
  1108. ret = new StringBuilder(rsize);
  1109. OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
  1110. value = ret.ToString ();
  1111. break;
  1112. case OciDataType.Long:
  1113. case OciDataType.LongVarChar:
  1114. int longSize = 0;
  1115. if (BitConverter.IsLittleEndian)
  1116. longSize = BitConverter.ToInt32 (new byte [] {bytes [0], bytes [1], bytes [2], bytes [3]}, 0);
  1117. else
  1118. longSize = BitConverter.ToInt32 (new byte [] {bytes [3], bytes [2], bytes [1], bytes [0]}, 0);
  1119. ASCIIEncoding encoding = new ASCIIEncoding ();
  1120. value = encoding.GetString (bytes, 4, longSize);
  1121. encoding = null;
  1122. break;
  1123. case OciDataType.Integer:
  1124. case OciDataType.Number:
  1125. case OciDataType.Float:
  1126. rsize = 0;
  1127. env = cmd.Connection.Environment;
  1128. OciCalls.OCICharSetToUnicode (env, null, bytes, out rsize);
  1129. // Get string
  1130. ret = new StringBuilder(rsize);
  1131. OciCalls.OCICharSetToUnicode (env, ret, bytes, out rsize);
  1132. // if not empty, parse string as a decimal using session format
  1133. if (ret.Length > 0)
  1134. value = Decimal.Parse (ret.ToString (), cmd.Connection.SessionFormatProvider);
  1135. break;
  1136. case OciDataType.TimeStamp:
  1137. value = dateTimeDesc.GetDateTime (connection.Environment, dateTimeDesc.ErrorHandle);
  1138. break;
  1139. case OciDataType.Date:
  1140. value = UnpackDate (bytes);
  1141. break;
  1142. case OciDataType.Blob:
  1143. case OciDataType.Clob:
  1144. if (value != null && value is OracleLob && value != OracleLob.Null) {
  1145. OracleLob lob2 = (OracleLob) value;
  1146. lob2.connection = connection;
  1147. }
  1148. else {
  1149. OracleLob lob = new OracleLob (lobLocator, ociType);
  1150. lob.connection = connection;
  1151. value = lob;
  1152. }
  1153. break;
  1154. case OciDataType.RSet: // REF CURSOR
  1155. OciStatementHandle cursorStatement = GetOutRefCursor (cmd);
  1156. value = new OracleDataReader (cursorStatement.Command, cursorStatement, true, CommandBehavior.Default);
  1157. break;
  1158. default:
  1159. throw new NotImplementedException ("Data Type not implemented: " + ociType.ToString() + ".");
  1160. }
  1161. }
  1162. internal OciStatementHandle GetOutRefCursor (OracleCommand cmd)
  1163. {
  1164. OciStatementHandle cursorStatement = new OciStatementHandle (cmd.Connection.ServiceContext, cursor);
  1165. cursorStatement.ErrorHandle = cmd.ErrorHandle;
  1166. cursorStatement.Command = cmd;
  1167. cursorStatement.SetupRefCursorResult (cmd.Connection);
  1168. cursorStatement.Service = cmd.Connection.ServiceContext;
  1169. cursor = IntPtr.Zero;
  1170. return cursorStatement;
  1171. }
  1172. internal void Update (OracleCommand cmd)
  1173. {
  1174. if (Direction != ParameterDirection.Input)
  1175. GetOutValue (cmd);
  1176. FreeHandle ();
  1177. }
  1178. internal void FreeHandle ()
  1179. {
  1180. switch (ociType) {
  1181. case OciDataType.Clob:
  1182. case OciDataType.Blob:
  1183. lobLocator = null;
  1184. break;
  1185. case OciDataType.Raw:
  1186. Marshal.FreeHGlobal (bindValue);
  1187. break;
  1188. case OciDataType.TimeStamp:
  1189. break;
  1190. default:
  1191. Marshal.FreeHGlobal (bindOutValue);
  1192. break;
  1193. }
  1194. bindOutValue = IntPtr.Zero;
  1195. bindValue = IntPtr.Zero;
  1196. bindHandle = null;
  1197. connection = null;
  1198. }
  1199. // copied from OciDefineHandle
  1200. [MonoTODO ("Be able to handle negative dates... i.e. BCE.")]
  1201. private DateTime UnpackDate (byte[] bytes)
  1202. {
  1203. byte century = bytes [0];
  1204. byte year = bytes [1];
  1205. byte month = bytes [2];
  1206. byte day = bytes [3];
  1207. byte hour = bytes [4];
  1208. byte minute = bytes [5];
  1209. byte second = bytes [6];
  1210. return new DateTime ((century - 100) * 100 + (year - 100),
  1211. month,
  1212. day,
  1213. hour - 1,
  1214. minute - 1,
  1215. second - 1);
  1216. }
  1217. private byte[] PackDate (DateTime dateValue)
  1218. {
  1219. byte[] buffer = new byte[7];
  1220. buffer[0] = (byte)((dateValue.Year / 100) + 100); //century
  1221. buffer[1] = (byte)((dateValue.Year % 100) + 100); // Year
  1222. buffer[2] = (byte)dateValue.Month;
  1223. buffer[3] = (byte)dateValue.Day;
  1224. buffer[4] = (byte)(dateValue.Hour+1);
  1225. buffer[5] = (byte)(dateValue.Minute+1);
  1226. buffer[6] = (byte)(dateValue.Second+1);
  1227. return buffer;
  1228. }
  1229. #endregion // Methods
  1230. internal sealed class OracleParameterConverter : ExpandableObjectConverter
  1231. {
  1232. public OracleParameterConverter ()
  1233. {
  1234. }
  1235. [MonoTODO]
  1236. public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType)
  1237. {
  1238. throw new NotImplementedException ();
  1239. }
  1240. [MonoTODO]
  1241. public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
  1242. {
  1243. throw new NotImplementedException ();
  1244. }
  1245. }
  1246. }
  1247. }