OdbcDataReader.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  1. //
  2. // System.Data.Odbc.OdbcDataReader
  3. //
  4. // Author:
  5. // Brian Ritchie ([email protected])
  6. // Daniel Morgan <[email protected]>
  7. // Sureshkumar T <[email protected]> (2004)
  8. //
  9. // Copyright (C) Brian Ritchie, 2002
  10. // Copyright (C) Daniel Morgan, 2002
  11. //
  12. //
  13. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  14. //
  15. // Permission is hereby granted, free of charge, to any person obtaining
  16. // a copy of this software and associated documentation files (the
  17. // "Software"), to deal in the Software without restriction, including
  18. // without limitation the rights to use, copy, modify, merge, publish,
  19. // distribute, sublicense, and/or sell copies of the Software, and to
  20. // permit persons to whom the Software is furnished to do so, subject to
  21. // the following conditions:
  22. //
  23. // The above copyright notice and this permission notice shall be
  24. // included in all copies or substantial portions of the Software.
  25. //
  26. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  27. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  28. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  29. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  30. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  31. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  32. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  33. //
  34. using System.Collections;
  35. using System.ComponentModel;
  36. using System.Data;
  37. using System.Data.Common;
  38. using System.Globalization;
  39. using System.Text;
  40. namespace System.Data.Odbc
  41. {
  42. #if NET_2_0
  43. public sealed class OdbcDataReader : DbDataReader
  44. #else
  45. public sealed class OdbcDataReader : MarshalByRefObject, IDataReader, IDisposable, IDataRecord, IEnumerable
  46. #endif
  47. {
  48. #region Fields
  49. private OdbcCommand command;
  50. private bool open;
  51. private int currentRow;
  52. private OdbcColumn[] cols;
  53. private IntPtr hstmt;
  54. private int _recordsAffected = -1;
  55. bool disposed;
  56. private DataTable _dataTableSchema;
  57. private CommandBehavior behavior;
  58. #endregion
  59. #region Constructors
  60. internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior)
  61. {
  62. this.command = command;
  63. this.CommandBehavior = behavior;
  64. open = true;
  65. currentRow = -1;
  66. hstmt = command.hStmt;
  67. // Init columns array;
  68. short colcount = 0;
  69. libodbc.SQLNumResultCols (hstmt, ref colcount);
  70. cols = new OdbcColumn [colcount];
  71. GetSchemaTable ();
  72. }
  73. internal OdbcDataReader (OdbcCommand command, CommandBehavior behavior,
  74. int recordAffected) : this (command, behavior)
  75. {
  76. _recordsAffected = recordAffected;
  77. }
  78. #endregion
  79. #region Properties
  80. private CommandBehavior CommandBehavior {
  81. get { return behavior; }
  82. set { value = behavior; }
  83. }
  84. public
  85. #if NET_2_0
  86. override
  87. #endif // NET_2_0
  88. int Depth {
  89. get {
  90. return 0; // no nested selects supported
  91. }
  92. }
  93. public
  94. #if NET_2_0
  95. override
  96. #endif // NET_2_0
  97. int FieldCount {
  98. get {
  99. return cols.Length;
  100. }
  101. }
  102. public
  103. #if NET_2_0
  104. override
  105. #endif // NET_2_0
  106. bool IsClosed {
  107. get {
  108. return !open;
  109. }
  110. }
  111. public
  112. #if NET_2_0
  113. override
  114. #endif // NET_2_0
  115. object this [string value] {
  116. get {
  117. int pos;
  118. if (currentRow == -1)
  119. throw new InvalidOperationException ();
  120. pos = ColIndex (value);
  121. if (pos == -1)
  122. throw new IndexOutOfRangeException ();
  123. return this [pos];
  124. }
  125. }
  126. public
  127. #if NET_2_0
  128. override
  129. #endif // NET_2_0
  130. object this [int i] {
  131. get {
  132. return (object) GetValue (i);
  133. }
  134. }
  135. public
  136. #if NET_2_0
  137. override
  138. #endif // NET_2_0
  139. int RecordsAffected {
  140. get {
  141. return _recordsAffected;
  142. }
  143. }
  144. [MonoTODO]
  145. public
  146. #if NET_2_0
  147. override
  148. #endif // NET_2_0
  149. bool HasRows {
  150. get { throw new NotImplementedException(); }
  151. }
  152. private OdbcConnection Connection {
  153. get {
  154. if (command != null)
  155. return command.Connection;
  156. return null;
  157. }
  158. }
  159. #endregion
  160. #region Methods
  161. private int ColIndex (string colname)
  162. {
  163. int i = 0;
  164. foreach (OdbcColumn col in cols) {
  165. if (col != null) {
  166. if (col.ColumnName == colname)
  167. return i;
  168. if (String.Compare (col.ColumnName, colname, true) == 0)
  169. return i;
  170. }
  171. i++;
  172. }
  173. return -1;
  174. }
  175. // Dynamically load column descriptions as needed.
  176. private OdbcColumn GetColumn (int ordinal)
  177. {
  178. if (cols [ordinal] == null) {
  179. short bufsize = 255;
  180. byte [] colname_buffer = new byte [bufsize];
  181. string colname;
  182. short colname_size = 0;
  183. uint ColSize = 0;
  184. short DecDigits = 0, Nullable = 0, dt = 0;
  185. OdbcReturn ret = libodbc.SQLDescribeCol (hstmt, Convert.ToUInt16 (ordinal + 1),
  186. colname_buffer, bufsize, ref colname_size, ref dt, ref ColSize,
  187. ref DecDigits, ref Nullable);
  188. if ((ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  189. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
  190. colname = RemoveTrailingNullChar (Encoding.Unicode.GetString (colname_buffer));
  191. OdbcColumn c = new OdbcColumn (colname, (SQL_TYPE) dt);
  192. c.AllowDBNull = (Nullable != 0);
  193. c.Digits = DecDigits;
  194. if (c.IsVariableSizeType)
  195. c.MaxLength = (int) ColSize;
  196. cols [ordinal] = c;
  197. }
  198. return cols [ordinal];
  199. }
  200. public
  201. #if NET_2_0
  202. override
  203. #endif // NET_2_0
  204. void Close ()
  205. {
  206. // FIXME : have to implement output parameter binding
  207. open = false;
  208. currentRow = -1;
  209. this.command.FreeIfNotPrepared ();
  210. if ((this.CommandBehavior & CommandBehavior.CloseConnection) == CommandBehavior.CloseConnection)
  211. this.command.Connection.Close ();
  212. }
  213. #if ONLY_1_1
  214. ~OdbcDataReader ()
  215. {
  216. this.Dispose (false);
  217. }
  218. #endif
  219. public
  220. #if NET_2_0
  221. override
  222. #endif // NET_2_0
  223. bool GetBoolean (int i)
  224. {
  225. return (bool) GetValue (i);
  226. }
  227. public
  228. #if NET_2_0
  229. override
  230. #endif // NET_2_0
  231. byte GetByte (int i)
  232. {
  233. return Convert.ToByte (GetValue (i));
  234. }
  235. public
  236. #if NET_2_0
  237. override
  238. #endif // NET_2_0
  239. long GetBytes (int i, long dataIndex, byte[] buffer, int bufferIndex, int length)
  240. {
  241. OdbcReturn ret = OdbcReturn.Error;
  242. bool copyBuffer = false;
  243. int returnVal = 0, outsize = 0;
  244. byte [] tbuff = new byte [length+1];
  245. length = buffer == null ? 0 : length;
  246. ret=libodbc.SQLGetData (hstmt, (ushort) (i + 1), SQL_C_TYPE.BINARY, tbuff, length,
  247. ref outsize);
  248. if (ret == OdbcReturn.NoData)
  249. return 0;
  250. if ( (ret != OdbcReturn.Success) && (ret != OdbcReturn.SuccessWithInfo))
  251. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
  252. OdbcException odbcException = null;
  253. if ( (ret == OdbcReturn.SuccessWithInfo))
  254. odbcException = Connection.CreateOdbcException (
  255. OdbcHandleType.Stmt, hstmt);
  256. if (buffer == null)
  257. return outsize; //if buffer is null,return length of the field
  258. if (ret == OdbcReturn.SuccessWithInfo) {
  259. if (outsize == (int) OdbcLengthIndicator.NoTotal)
  260. copyBuffer = true;
  261. else if (outsize == (int) OdbcLengthIndicator.NullData) {
  262. copyBuffer = false;
  263. returnVal = -1;
  264. } else {
  265. string sqlstate = odbcException.Errors [0].SQLState;
  266. //SQLState: String Data, Right truncated
  267. if (sqlstate != libodbc.SQLSTATE_RIGHT_TRUNC)
  268. throw odbcException;
  269. copyBuffer = true;
  270. }
  271. } else {
  272. copyBuffer = outsize == -1 ? false : true;
  273. returnVal = outsize;
  274. }
  275. if (copyBuffer) {
  276. int j = 0;
  277. while (tbuff [j] != libodbc.C_NULL) {
  278. buffer [bufferIndex + i] = tbuff [j];
  279. j++;
  280. }
  281. returnVal = j;
  282. }
  283. return returnVal;
  284. }
  285. [MonoTODO]
  286. public
  287. #if NET_2_0
  288. override
  289. #endif // NET_2_0
  290. char GetChar (int i)
  291. {
  292. throw new NotImplementedException ();
  293. }
  294. [MonoTODO]
  295. public
  296. #if NET_2_0
  297. override
  298. #endif // NET_2_0
  299. long GetChars (int i, long dataIndex, char[] buffer, int bufferIndex, int length)
  300. {
  301. throw new NotImplementedException ();
  302. }
  303. [MonoTODO]
  304. [EditorBrowsableAttribute (EditorBrowsableState.Never)]
  305. #if ONLY_1_1
  306. public
  307. #endif
  308. #if NET_2_0
  309. new
  310. #endif
  311. IDataReader GetData (int i)
  312. {
  313. throw new NotImplementedException ();
  314. }
  315. public
  316. #if NET_2_0
  317. override
  318. #endif // NET_2_0
  319. string GetDataTypeName (int i)
  320. {
  321. return GetColumnAttributeStr (i + 1, FieldIdentifier.TypeName);
  322. }
  323. public DateTime GetDate (int i)
  324. {
  325. return GetDateTime (i);
  326. }
  327. public
  328. #if NET_2_0
  329. override
  330. #endif // NET_2_0
  331. DateTime GetDateTime (int i)
  332. {
  333. return (DateTime) GetValue (i);
  334. }
  335. public
  336. #if NET_2_0
  337. override
  338. #endif // NET_2_0
  339. decimal GetDecimal (int i)
  340. {
  341. return (decimal) GetValue (i);
  342. }
  343. public
  344. #if NET_2_0
  345. override
  346. #endif // NET_2_0
  347. double GetDouble (int i)
  348. {
  349. return (double) GetValue (i);
  350. }
  351. public
  352. #if NET_2_0
  353. override
  354. #endif // NET_2_0
  355. Type GetFieldType (int i)
  356. {
  357. return GetColumn (i).DataType;
  358. }
  359. public
  360. #if NET_2_0
  361. override
  362. #endif // NET_2_0
  363. float GetFloat (int i)
  364. {
  365. return (float) GetValue (i);
  366. }
  367. [MonoTODO]
  368. public
  369. #if NET_2_0
  370. override
  371. #endif // NET_2_0
  372. Guid GetGuid (int i)
  373. {
  374. throw new NotImplementedException ();
  375. }
  376. public
  377. #if NET_2_0
  378. override
  379. #endif // NET_2_0
  380. short GetInt16 (int i)
  381. {
  382. return (short) GetValue (i);
  383. }
  384. public
  385. #if NET_2_0
  386. override
  387. #endif // NET_2_0
  388. int GetInt32 (int i)
  389. {
  390. return (int) GetValue (i);
  391. }
  392. public
  393. #if NET_2_0
  394. override
  395. #endif // NET_2_0
  396. long GetInt64 (int i)
  397. {
  398. return (long) GetValue (i);
  399. }
  400. public
  401. #if NET_2_0
  402. override
  403. #endif // NET_2_0
  404. string GetName (int i)
  405. {
  406. return GetColumn (i).ColumnName;
  407. }
  408. public
  409. #if NET_2_0
  410. override
  411. #endif // NET_2_0
  412. int GetOrdinal (string value)
  413. {
  414. int i = ColIndex (value);
  415. if (i == -1)
  416. throw new IndexOutOfRangeException ();
  417. else
  418. return i;
  419. }
  420. [MonoTODO]
  421. public
  422. #if NET_2_0
  423. override
  424. #endif // NET_2_0
  425. DataTable GetSchemaTable ()
  426. {
  427. // FIXME :
  428. // * Map OdbcType to System.Type and assign to DataType.
  429. // This will eliminate the need for IsStringType in
  430. // OdbcColumn
  431. if (_dataTableSchema != null)
  432. return _dataTableSchema;
  433. DataTable dataTableSchema = null;
  434. // Only Results from SQL SELECT Queries
  435. // get a DataTable for schema of the result
  436. // otherwise, DataTable is null reference
  437. if (cols.Length > 0) {
  438. dataTableSchema = new DataTable ();
  439. dataTableSchema.Columns.Add ("ColumnName", typeof (string));
  440. dataTableSchema.Columns.Add ("ColumnOrdinal", typeof (int));
  441. dataTableSchema.Columns.Add ("ColumnSize", typeof (int));
  442. dataTableSchema.Columns.Add ("NumericPrecision", typeof (int));
  443. dataTableSchema.Columns.Add ("NumericScale", typeof (int));
  444. dataTableSchema.Columns.Add ("IsUnique", typeof (bool));
  445. dataTableSchema.Columns.Add ("IsKey", typeof (bool));
  446. DataColumn dc = dataTableSchema.Columns["IsKey"];
  447. dc.AllowDBNull = true; // IsKey can have a DBNull
  448. dataTableSchema.Columns.Add ("BaseCatalogName", typeof (string));
  449. dataTableSchema.Columns.Add ("BaseColumnName", typeof (string));
  450. dataTableSchema.Columns.Add ("BaseSchemaName", typeof (string));
  451. dataTableSchema.Columns.Add ("BaseTableName", typeof (string));
  452. dataTableSchema.Columns.Add ("DataType", typeof(Type));
  453. dataTableSchema.Columns.Add ("AllowDBNull", typeof (bool));
  454. dataTableSchema.Columns.Add ("ProviderType", typeof (int));
  455. dataTableSchema.Columns.Add ("IsAliased", typeof (bool));
  456. dataTableSchema.Columns.Add ("IsExpression", typeof (bool));
  457. dataTableSchema.Columns.Add ("IsIdentity", typeof (bool));
  458. dataTableSchema.Columns.Add ("IsAutoIncrement", typeof (bool));
  459. dataTableSchema.Columns.Add ("IsRowVersion", typeof (bool));
  460. dataTableSchema.Columns.Add ("IsHidden", typeof (bool));
  461. dataTableSchema.Columns.Add ("IsLong", typeof (bool));
  462. dataTableSchema.Columns.Add ("IsReadOnly", typeof (bool));
  463. DataRow schemaRow;
  464. for (int i = 0; i < cols.Length; i += 1 ) {
  465. string baseTableName = String.Empty;
  466. bool isKey = false;
  467. OdbcColumn col=GetColumn(i);
  468. schemaRow = dataTableSchema.NewRow ();
  469. dataTableSchema.Rows.Add (schemaRow);
  470. schemaRow ["ColumnName"] = col.ColumnName;
  471. schemaRow ["ColumnOrdinal"] = i;
  472. schemaRow ["ColumnSize"] = col.MaxLength;
  473. schemaRow ["NumericPrecision"] = GetColumnAttribute (i+1, FieldIdentifier.Precision);
  474. schemaRow ["NumericScale"] = GetColumnAttribute (i+1, FieldIdentifier.Scale);
  475. schemaRow ["BaseTableName"] = GetColumnAttributeStr (i+1, FieldIdentifier.TableName);
  476. schemaRow ["BaseSchemaName"] = GetColumnAttributeStr (i+1, FieldIdentifier.SchemaName);
  477. schemaRow ["BaseCatalogName"] = GetColumnAttributeStr (i+1, FieldIdentifier.CatelogName);
  478. schemaRow ["BaseColumnName"] = GetColumnAttributeStr (i+1, FieldIdentifier.BaseColumnName);
  479. schemaRow ["DataType"] = col.DataType;
  480. schemaRow ["IsUnique"] = false;
  481. schemaRow ["IsKey"] = DBNull.Value;
  482. schemaRow ["AllowDBNull"] = GetColumnAttribute (i+1, FieldIdentifier.Nullable) != libodbc.SQL_NO_NULLS;
  483. schemaRow ["ProviderType"] = (int) col.OdbcType;
  484. schemaRow ["IsAutoIncrement"] = GetColumnAttribute (i+1, FieldIdentifier.AutoUniqueValue) == libodbc.SQL_TRUE;
  485. schemaRow ["IsExpression"] = schemaRow.IsNull ("BaseTableName") || (string) schemaRow ["BaseTableName"] == String.Empty;
  486. schemaRow ["IsAliased"] = (string) schemaRow ["BaseColumnName"] != (string) schemaRow ["ColumnName"];
  487. schemaRow ["IsReadOnly"] = ((bool) schemaRow ["IsExpression"]
  488. || GetColumnAttribute (i+1, FieldIdentifier.Updatable) == libodbc.SQL_ATTR_READONLY);
  489. // FIXME: all of these
  490. schemaRow ["IsIdentity"] = false;
  491. schemaRow ["IsRowVersion"] = false;
  492. schemaRow ["IsHidden"] = false;
  493. schemaRow ["IsLong"] = false;
  494. // FIXME: according to Brian,
  495. // this does not work on MS .NET
  496. // however, we need it for Mono
  497. // for now
  498. // schemaRow.AcceptChanges();
  499. }
  500. // set primary keys
  501. DataRow [] rows = dataTableSchema.Select ("BaseTableName <> ''",
  502. "BaseCatalogName, BaseSchemaName, BaseTableName ASC");
  503. string lastTableName = String.Empty,
  504. lastSchemaName = String.Empty,
  505. lastCatalogName = String.Empty;
  506. string [] keys = null; // assumed to be sorted.
  507. foreach (DataRow row in rows) {
  508. string tableName = (string) row ["BaseTableName"];
  509. string schemaName = (string) row ["BaseSchemaName"];
  510. string catalogName = (string) row ["BaseCatalogName"];
  511. if (tableName != lastTableName || schemaName != lastSchemaName
  512. || catalogName != lastCatalogName)
  513. keys = GetPrimaryKeys (catalogName, schemaName, tableName);
  514. if (keys != null &&
  515. Array.BinarySearch (keys, (string) row ["BaseColumnName"]) >= 0) {
  516. row ["IsKey"] = true;
  517. row ["IsUnique"] = true;
  518. row ["AllowDBNull"] = false;
  519. GetColumn ( ColIndex ( (string) row ["ColumnName"])).AllowDBNull = false;
  520. }
  521. lastTableName = tableName;
  522. lastSchemaName = schemaName;
  523. lastCatalogName = catalogName;
  524. }
  525. dataTableSchema.AcceptChanges ();
  526. }
  527. return (_dataTableSchema = dataTableSchema);
  528. }
  529. public
  530. #if NET_2_0
  531. override
  532. #endif // NET_2_0
  533. string GetString (int i)
  534. {
  535. object ret = GetValue (i);
  536. if (ret != null && ret.GetType () != typeof (string))
  537. return Convert.ToString (ret);
  538. else
  539. return (string) GetValue (i);
  540. }
  541. [MonoTODO]
  542. public TimeSpan GetTime (int i)
  543. {
  544. throw new NotImplementedException ();
  545. }
  546. public
  547. #if NET_2_0
  548. override
  549. #endif // NET_2_0
  550. object GetValue (int i)
  551. {
  552. if (currentRow == -1)
  553. throw new IndexOutOfRangeException ();
  554. if (i > cols.Length-1 || i < 0)
  555. throw new IndexOutOfRangeException ();
  556. OdbcReturn ret;
  557. int outsize = 0, bufsize;
  558. byte[] buffer;
  559. OdbcColumn col = GetColumn (i);
  560. object DataValue = null;
  561. ushort ColIndex = Convert.ToUInt16 (i + 1);
  562. // Check cached values
  563. if (col.Value == null) {
  564. // odbc help file
  565. // mk:@MSITStore:C:\program%20files\Microsoft%20Data%20Access%20SDK\Docs\odbc.chm::/htm/odbcc_data_types.htm
  566. switch (col.OdbcType) {
  567. case OdbcType.Bit:
  568. short bit_data = 0;
  569. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref bit_data, 0, ref outsize);
  570. if (outsize != (int) OdbcLengthIndicator.NullData)
  571. DataValue = bit_data == 0 ? "False" : "True";
  572. break;
  573. case OdbcType.Numeric:
  574. case OdbcType.Decimal:
  575. bufsize = 50;
  576. buffer = new byte [bufsize]; // According to sqlext.h, use SQL_CHAR for decimal.
  577. // FIXME : use Numeric.
  578. ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
  579. if (outsize!=-1) {
  580. byte [] temp = new byte [outsize];
  581. for (int j = 0; j < outsize; j++)
  582. temp [j] = buffer [j];
  583. DataValue = Decimal.Parse (Encoding.Default.GetString (temp),
  584. CultureInfo.InvariantCulture);
  585. }
  586. break;
  587. case OdbcType.TinyInt:
  588. short short_data = 0;
  589. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref short_data, 0, ref outsize);
  590. DataValue = Convert.ToByte (short_data);
  591. break;
  592. case OdbcType.Int:
  593. int int_data = 0;
  594. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref int_data, 0, ref outsize);
  595. DataValue = int_data;
  596. break;
  597. case OdbcType.SmallInt:
  598. short sint_data = 0;
  599. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref sint_data, 0, ref outsize);
  600. DataValue = sint_data;
  601. break;
  602. case OdbcType.BigInt:
  603. long long_data = 0;
  604. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref long_data, 0, ref outsize);
  605. DataValue = long_data;
  606. break;
  607. case OdbcType.NChar:
  608. bufsize = 255;
  609. buffer = new byte [bufsize];
  610. ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.WCHAR, buffer, bufsize, ref outsize);
  611. if (outsize != (int) OdbcLengthIndicator.NullData)
  612. if (!(ret == OdbcReturn.SuccessWithInfo
  613. && outsize == (int) OdbcLengthIndicator.NoTotal))
  614. DataValue = Encoding.Unicode.GetString (buffer, 0, outsize);
  615. break;
  616. case OdbcType.NText:
  617. case OdbcType.NVarChar:
  618. bufsize = (col.MaxLength < 127 ? (col.MaxLength*2+1) : 255);
  619. buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
  620. StringBuilder sb = new StringBuilder ();
  621. do {
  622. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
  623. if (ret == OdbcReturn.Error)
  624. break;
  625. // Fix for strance ODBC drivers (like psqlODBC)
  626. if (ret == OdbcReturn.Success && outsize==-1)
  627. ret = OdbcReturn.NoData;
  628. if (ret != OdbcReturn.NoData && outsize > 0) {
  629. string value = null;
  630. if (outsize < bufsize)
  631. value = Encoding.Unicode.GetString (buffer, 0, outsize);
  632. else
  633. value = Encoding.Unicode.GetString (buffer, 0, bufsize);
  634. sb.Append (RemoveTrailingNullChar (value));
  635. }
  636. } while (ret != OdbcReturn.NoData);
  637. DataValue = sb.ToString ();
  638. break;
  639. case OdbcType.Text:
  640. case OdbcType.VarChar:
  641. bufsize = (col.MaxLength < 255 ? (col.MaxLength+1) : 255);
  642. buffer = new byte[bufsize]; // According to sqlext.h, use SQL_CHAR for both char and varchar
  643. StringBuilder sb1 = new StringBuilder ();
  644. do {
  645. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, buffer, bufsize, ref outsize);
  646. if (ret == OdbcReturn.Error)
  647. break;
  648. // Fix for strance ODBC drivers (like psqlODBC)
  649. if (ret == OdbcReturn.Success && outsize==-1)
  650. ret = OdbcReturn.NoData;
  651. if (ret != OdbcReturn.NoData && outsize > 0) {
  652. if (outsize < bufsize)
  653. sb1.Append (Encoding.Default.GetString (buffer, 0, outsize));
  654. else
  655. sb1.Append (Encoding.Default.GetString (buffer, 0, bufsize - 1));
  656. }
  657. } while (ret != OdbcReturn.NoData);
  658. DataValue = sb1.ToString ();
  659. break;
  660. case OdbcType.Real:
  661. float float_data = 0;
  662. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref float_data, 0, ref outsize);
  663. DataValue = float_data;
  664. break;
  665. case OdbcType.Double:
  666. double double_data = 0;
  667. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref double_data, 0, ref outsize);
  668. DataValue = double_data;
  669. break;
  670. case OdbcType.Timestamp:
  671. case OdbcType.DateTime:
  672. case OdbcType.Date:
  673. case OdbcType.Time:
  674. OdbcTimestamp ts_data = new OdbcTimestamp();
  675. ret = libodbc.SQLGetData (hstmt, ColIndex, col.SqlCType, ref ts_data, 0, ref outsize);
  676. if (outsize != -1) {// This means SQL_NULL_DATA
  677. if (col.OdbcType == OdbcType.Time) {
  678. // libodbc returns value in first three fields for OdbcType.Time
  679. DataValue = new System.TimeSpan (ts_data.year, ts_data.month, ts_data.day);
  680. } else {
  681. DataValue = new DateTime(ts_data.year, ts_data.month,
  682. ts_data.day, ts_data.hour, ts_data.minute,
  683. ts_data.second);
  684. if (ts_data.fraction != 0)
  685. DataValue = ((DateTime) DataValue).AddTicks ((long)ts_data.fraction / 100);
  686. }
  687. }
  688. break;
  689. case OdbcType.VarBinary :
  690. case OdbcType.Image :
  691. bufsize = (col.MaxLength < 255 && col.MaxLength > 0 ? col.MaxLength : 255);
  692. buffer= new byte [bufsize];
  693. ArrayList al = new ArrayList ();
  694. do {
  695. ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.BINARY, buffer, bufsize, ref outsize);
  696. if (ret == OdbcReturn.Error)
  697. break;
  698. if (ret != OdbcReturn.NoData && outsize!=-1) {
  699. if (outsize < bufsize) {
  700. byte[] tmparr = new byte [outsize];
  701. Array.Copy (buffer, 0, tmparr, 0, outsize);
  702. al.AddRange (tmparr);
  703. } else
  704. al.AddRange (buffer);
  705. }
  706. } while (ret != OdbcReturn.NoData);
  707. DataValue = al.ToArray (typeof (byte));
  708. break;
  709. case OdbcType.Binary :
  710. bufsize = col.MaxLength;
  711. buffer = new byte [bufsize];
  712. long read = GetBytes (i, 0, buffer, 0, bufsize);
  713. ret = OdbcReturn.Success;
  714. DataValue = buffer;
  715. break;
  716. default:
  717. bufsize = 255;
  718. buffer = new byte[bufsize];
  719. ret = libodbc.SQLGetData (hstmt, ColIndex, SQL_C_TYPE.CHAR, buffer, bufsize, ref outsize);
  720. if (outsize != (int) OdbcLengthIndicator.NullData)
  721. if (! (ret == OdbcReturn.SuccessWithInfo
  722. && outsize == (int) OdbcLengthIndicator.NoTotal))
  723. DataValue = Encoding.Default.GetString (buffer, 0, outsize);
  724. break;
  725. }
  726. if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo) && (ret!=OdbcReturn.NoData))
  727. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, hstmt);
  728. if (outsize == -1) // This means SQL_NULL_DATA
  729. col.Value = DBNull.Value;
  730. else
  731. col.Value = DataValue;
  732. }
  733. return col.Value;
  734. }
  735. public
  736. #if NET_2_0
  737. override
  738. #endif // NET_2_0
  739. int GetValues (object [] values)
  740. {
  741. int numValues = 0;
  742. // copy values
  743. for (int i = 0; i < values.Length; i++) {
  744. if (i < FieldCount) {
  745. values [i] = GetValue (i);
  746. } else {
  747. values [i] = null;
  748. }
  749. }
  750. // get number of object instances in array
  751. if (values.Length < FieldCount)
  752. numValues = values.Length;
  753. else if (values.Length == FieldCount)
  754. numValues = FieldCount;
  755. else
  756. numValues = FieldCount;
  757. return numValues;
  758. }
  759. #if ONLY_1_1
  760. void IDisposable.Dispose ()
  761. {
  762. Dispose (true);
  763. GC.SuppressFinalize (this);
  764. }
  765. IEnumerator IEnumerable.GetEnumerator ()
  766. {
  767. return new DbEnumerator (this);
  768. }
  769. #endif // ONLY_1_1
  770. #if NET_2_0
  771. public override IEnumerator GetEnumerator ()
  772. {
  773. return new DbEnumerator (this);
  774. }
  775. #endif
  776. #if NET_2_0
  777. protected override
  778. #endif
  779. void Dispose (bool disposing)
  780. {
  781. if (disposed)
  782. return;
  783. if (disposing) {
  784. // dispose managed resources
  785. Close ();
  786. }
  787. command = null;
  788. cols = null;
  789. _dataTableSchema = null;
  790. disposed = true;
  791. }
  792. public
  793. #if NET_2_0
  794. override
  795. #endif // NET_2_0
  796. bool IsDBNull (int i)
  797. {
  798. return (GetValue (i) is DBNull);
  799. }
  800. /// <remarks>
  801. /// Move to the next result set.
  802. /// </remarks>
  803. public
  804. #if NET_2_0
  805. override
  806. #endif // NET_2_0
  807. bool NextResult ()
  808. {
  809. OdbcReturn ret = OdbcReturn.Success;
  810. ret = libodbc.SQLMoreResults (hstmt);
  811. if (ret == OdbcReturn.Success) {
  812. short colcount = 0;
  813. libodbc.SQLNumResultCols (hstmt, ref colcount);
  814. cols = new OdbcColumn [colcount];
  815. _dataTableSchema = null; // force fresh creation
  816. GetSchemaTable ();
  817. }
  818. return (ret == OdbcReturn.Success);
  819. }
  820. /// <remarks>
  821. /// Load the next row in the current result set.
  822. /// </remarks>
  823. private bool NextRow ()
  824. {
  825. OdbcReturn ret = libodbc.SQLFetch (hstmt);
  826. if (ret != OdbcReturn.Success)
  827. currentRow = -1;
  828. else
  829. currentRow++;
  830. // Clear cached values from last record
  831. foreach (OdbcColumn col in cols) {
  832. if (col != null)
  833. col.Value = null;
  834. }
  835. return (ret == OdbcReturn.Success);
  836. }
  837. private int GetColumnAttribute (int column, FieldIdentifier fieldId)
  838. {
  839. OdbcReturn ret = OdbcReturn.Error;
  840. byte [] buffer = new byte [255];
  841. short outsize = 0;
  842. int val = 0;
  843. ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
  844. buffer, (short)buffer.Length,
  845. ref outsize, ref val);
  846. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  847. throw Connection.CreateOdbcException (
  848. OdbcHandleType.Stmt, hstmt);
  849. return val;
  850. }
  851. private string GetColumnAttributeStr (int column, FieldIdentifier fieldId)
  852. {
  853. OdbcReturn ret = OdbcReturn.Error;
  854. byte [] buffer = new byte [255];
  855. short outsize = 0;
  856. int val = 0;
  857. ret = libodbc.SQLColAttribute (hstmt, (short)column, fieldId,
  858. buffer, (short)buffer.Length,
  859. ref outsize, ref val);
  860. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  861. throw Connection.CreateOdbcException (
  862. OdbcHandleType.Stmt, hstmt);
  863. string value = string.Empty;
  864. if (outsize > 0)
  865. value = Encoding.Unicode.GetString (buffer, 0, outsize);
  866. return value;
  867. }
  868. private string [] GetPrimaryKeys (string catalog, string schema, string table)
  869. {
  870. if (cols.Length <= 0)
  871. return new string [0];
  872. ArrayList keys = null;
  873. try {
  874. keys = GetPrimaryKeysBySQLPrimaryKey (catalog, schema, table);
  875. } catch (OdbcException) {
  876. try {
  877. keys = GetPrimaryKeysBySQLStatistics (catalog, schema, table);
  878. } catch (OdbcException) {
  879. }
  880. }
  881. if (keys == null)
  882. return null;
  883. keys.Sort ();
  884. return (string []) keys.ToArray (typeof (string));
  885. }
  886. private ArrayList GetPrimaryKeysBySQLPrimaryKey (string catalog, string schema, string table)
  887. {
  888. ArrayList keys = new ArrayList ();
  889. IntPtr handle = IntPtr.Zero;
  890. OdbcReturn ret;
  891. try {
  892. ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
  893. command.Connection.hDbc, ref handle);
  894. if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
  895. throw Connection.CreateOdbcException (
  896. OdbcHandleType.Dbc, Connection.hDbc);
  897. ret = libodbc.SQLPrimaryKeys (handle, catalog, -3,
  898. schema, -3, table, -3);
  899. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  900. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  901. int length = 0;
  902. byte [] primaryKey = new byte [255];
  903. ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.CHAR, primaryKey, primaryKey.Length, ref length);
  904. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  905. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  906. int i = 0;
  907. while (true) {
  908. ret = libodbc.SQLFetch (handle);
  909. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  910. break;
  911. string pkey = Encoding.Default.GetString (primaryKey, 0, length);
  912. keys.Add (pkey);
  913. }
  914. } finally {
  915. if (handle != IntPtr.Zero) {
  916. ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
  917. if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
  918. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  919. ret = libodbc.SQLFreeHandle( (ushort) OdbcHandleType.Stmt, handle);
  920. if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
  921. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  922. }
  923. }
  924. return keys;
  925. }
  926. private unsafe ArrayList GetPrimaryKeysBySQLStatistics (string catalog, string schema, string table)
  927. {
  928. ArrayList keys = new ArrayList ();
  929. IntPtr handle = IntPtr.Zero;
  930. OdbcReturn ret;
  931. try {
  932. ret=libodbc.SQLAllocHandle(OdbcHandleType.Stmt,
  933. command.Connection.hDbc, ref handle);
  934. if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
  935. throw Connection.CreateOdbcException (
  936. OdbcHandleType.Dbc, Connection.hDbc);
  937. ret = libodbc.SQLStatistics (handle, catalog, -3,
  938. schema, -3,
  939. table, -3,
  940. libodbc.SQL_INDEX_UNIQUE,
  941. libodbc.SQL_QUICK);
  942. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  943. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  944. // NON_UNIQUE
  945. int nonUniqueLength = 0;
  946. short nonUnique = libodbc.SQL_FALSE;
  947. ret = libodbc.SQLBindCol (handle, 4, SQL_C_TYPE.SHORT, ref (short) nonUnique, sizeof (short), ref nonUniqueLength);
  948. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  949. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  950. // COLUMN_NAME
  951. int length = 0;
  952. byte [] colName = new byte [255];
  953. ret = libodbc.SQLBindCol (handle, 9, SQL_C_TYPE.CHAR, colName, colName.Length, ref length);
  954. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  955. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  956. int i = 0;
  957. while (true) {
  958. ret = libodbc.SQLFetch (handle);
  959. if (ret != OdbcReturn.Success && ret != OdbcReturn.SuccessWithInfo)
  960. break;
  961. if (nonUnique == libodbc.SQL_TRUE) {
  962. string pkey = Encoding.Default.GetString (colName, 0, length);
  963. keys.Add (pkey);
  964. break;
  965. }
  966. }
  967. } finally {
  968. if (handle != IntPtr.Zero) {
  969. ret = libodbc.SQLFreeStmt (handle, libodbc.SQLFreeStmtOptions.Close);
  970. if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
  971. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  972. ret = libodbc.SQLFreeHandle ((ushort) OdbcHandleType.Stmt, handle);
  973. if ((ret!=OdbcReturn.Success) && (ret!=OdbcReturn.SuccessWithInfo))
  974. throw Connection.CreateOdbcException (OdbcHandleType.Stmt, handle);
  975. }
  976. }
  977. return keys;
  978. }
  979. public
  980. #if NET_2_0
  981. override
  982. #endif // NET_2_0
  983. bool Read ()
  984. {
  985. return NextRow ();
  986. }
  987. static string RemoveTrailingNullChar (string value)
  988. {
  989. return value.TrimEnd ('\0');
  990. }
  991. #endregion
  992. }
  993. }