Missed a few places still referring to com.versabanq.versaplex.
[versaplex.git] / versaplexd / t / versaplex.t.cs
blob707114e3311728a36682ddafd9c05e0367a62527
1 #include "wvtest.cs.h"
3 using Mono.Unix;
4 using System;
5 using System.Data;
6 using System.Data.SqlClient;
7 using System.Data.SqlTypes;
8 using System.Collections.Generic;
9 using System.IO;
10 using System.Net;
11 using System.Net.Sockets;
12 using Wv.Test;
13 using Wv;
14 using NDesk.DBus;
16 // Several mono bugs worked around in this test fixture are filed as mono bug
17 // #81940
19 // Versaplex now needs DBus clients to provide their Unix UID to the bus.
20 // FIXME: This is mostly duplicated from Main.cs, as it's a bit tricky to share
21 // classes between Versaplex and the tests.
22 class DodgyTransport : NDesk.DBus.Transports.Transport
24 public override string AuthString()
26 long uid = UnixUserInfo.GetRealUserId();
27 return uid.ToString();
30 public override void WriteCred()
32 Stream.WriteByte(0);
35 public override void Open(AddressEntry entry)
37 Socket sock;
38 if (entry.Method == "unix")
40 string path;
41 bool abstr;
43 if (entry.Properties.TryGetValue("path", out path))
44 abstr = false;
45 else if (entry.Properties.TryGetValue("abstract", out path))
46 abstr = true;
47 else
48 throw new Exception("No path specified for UNIX transport");
50 if (abstr)
51 sock = OpenAbstractUnix(path);
52 else
53 sock = OpenPathUnix(path);
55 else if (entry.Method == "tcp")
57 string host = "127.0.0.1";
58 string port = "5555";
59 entry.Properties.TryGetValue("host", out host);
60 entry.Properties.TryGetValue("port", out port);
61 sock = OpenTcp(host, Int32.Parse(port));
63 else
64 throw new Exception(String.Format("Unknown connection method {0}",
65 entry.Method));
67 sock.Blocking = true;
68 SocketHandle = (long)sock.Handle;
69 Stream = new NetworkStream(sock);
72 protected Socket OpenAbstractUnix(string path)
74 AbstractUnixEndPoint ep = new AbstractUnixEndPoint(path);
75 Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, 0);
76 client.Connect(ep);
77 return client;
80 public Socket OpenPathUnix(string path)
82 UnixEndPoint ep = new UnixEndPoint(path);
83 Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, 0);
84 client.Connect(ep);
85 return client;
88 public Socket OpenTcp(string host, int port)
90 IPHostEntry hent = Dns.GetHostEntry(host);
91 IPAddress ip = hent.AddressList[0];
92 IPEndPoint ep = new IPEndPoint(ip, port);
93 Socket client = new Socket(AddressFamily.InterNetwork,
94 SocketType.Stream, 0);
95 client.Connect(ep);
96 return client;
100 [TestFixture]
101 public class VersaplexTest: IDisposable
103 private const string DbusConnName = "vx.versaplexd";
104 private const string DbusInterface = "vx.db";
105 private static readonly ObjectPath DbusObjPath;
107 static VersaplexTest() {
108 DbusObjPath = new ObjectPath("/db");
111 // A file full of "lorem ipsum dolor" text
112 private const string lipsum_file = "lipsum.txt";
113 // A UTF-8 test file
114 private const string unicode_file = "UTF-8-demo.txt";
115 // A random file of binary goop
116 private const string goop_file = "random.bin";
117 // THTBACS image
118 private const string image_file = "thtbacs.tiff";
120 SqlConnection con;
121 SqlCommand cmd;
122 Bus bus;
124 public VersaplexTest()
126 // Places to look for the config file.
127 string [] searchfiles =
129 "versaplexd.ini",
130 Path.Combine("..", "versaplexd.ini")
133 string cfgfile = null;
134 foreach (string searchfile in searchfiles)
135 if (File.Exists(searchfile))
136 cfgfile = searchfile;
138 if (cfgfile == null)
139 throw new Exception("Cannot locate versaplexd.ini.");
141 WvIni cfg = new WvIni(cfgfile);
143 string uname = Mono.Unix.UnixUserInfo.GetRealUser().UserName;
144 string dbname = cfg["User Map"][uname];
145 if (dbname == null)
146 dbname = cfg["User Map"]["*"];
147 if (dbname == null)
148 throw new Exception(String.Format(
149 "User '{0}' (and '*') missing from config.", uname));
151 string cfgval = cfg["Connections"][dbname];
152 if (cfgval == null)
153 throw new Exception(String.Format(
154 "Connection string for '{0}' missing from config.", dbname));
156 string mssql_moniker = "mssql:";
157 if (cfgval.IndexOf(mssql_moniker) == 0)
158 con = new SqlConnection(cfgval.Substring(mssql_moniker.Length));
159 else
160 throw new Exception(String.Format(
161 "Malformed connection string '{0}'.", cfgval));
163 WVASSERT(Connect(con));
165 cmd = con.CreateCommand();
167 if (Address.Session == null)
168 throw new Exception ("DBUS_SESSION_BUS_ADDRESS not set");
169 AddressEntry aent = AddressEntry.Parse(Address.Session);
170 DodgyTransport trans = new DodgyTransport();
171 trans.Open(aent);
172 bus = new Bus(trans);
175 public void Dispose()
177 bus = null;
179 if (cmd != null)
180 cmd.Dispose();
181 cmd = null;
183 if (con != null)
184 con.Dispose();
185 con = null;
188 bool Connect(SqlConnection connection)
190 connection.Open();
192 return true;
195 bool Exec(string query)
197 Console.WriteLine(" + Exec SQL Query: {0}", query);
199 using (SqlCommand execCmd = new SqlCommand(query, con)) {
200 execCmd.ExecuteNonQuery();
203 return true;
206 bool Scalar(string query, out object result)
208 Console.WriteLine(" + Scalar SQL Query: {0}", query);
210 using (SqlCommand execCmd = new SqlCommand(query, con)) {
211 result = execCmd.ExecuteScalar();
214 return true;
217 bool Reader(string query, out SqlDataReader result)
219 Console.WriteLine(" + Reader SQL Query: {0}", query);
221 using (SqlCommand execCmd = new SqlCommand(query, con)) {
222 result = execCmd.ExecuteReader();
225 return true;
228 Message CreateMethodCall(string destination, ObjectPath path,
229 string iface, string member, string signature)
231 Message msg = new Message();
232 msg.Connection = bus;
233 msg.Header.MessageType = MessageType.MethodCall;
234 msg.Header.Flags = HeaderFlag.None;
235 msg.Header.Fields[FieldCode.Path] = path;
236 msg.Header.Fields[FieldCode.Member] = member;
238 if (destination != null && destination != "")
239 msg.Header.Fields[FieldCode.Destination] = destination;
241 if (iface != null && iface != "")
242 msg.Header.Fields[FieldCode.Interface] = iface;
244 if (signature != null && signature != "")
245 msg.Header.Fields[FieldCode.Signature] = new Signature(signature);
247 return msg;
250 bool VxExec(string query)
252 Console.WriteLine(" + VxExec SQL Query: {0}", query);
254 Message call = CreateMethodCall(DbusConnName, DbusObjPath,
255 DbusInterface, "ExecNoResult", "s");
257 MessageWriter mw = new MessageWriter(Connection.NativeEndianness);
258 mw.Write(typeof(string), query);
260 call.Body = mw.ToArray();
262 Message reply = bus.SendWithReplyAndBlock(call);
264 switch (reply.Header.MessageType) {
265 case MessageType.MethodReturn:
266 return true;
267 case MessageType.Error:
269 object errname;
270 if (!reply.Header.Fields.TryGetValue(FieldCode.ErrorName,
271 out errname))
272 throw new Exception("D-Bus error received but no error name "
273 +"given");
275 object errsig;
276 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
277 out errsig) || errsig.ToString() != "s")
278 throw new DbusError(errname.ToString());
280 MessageReader mr = new MessageReader(reply);
282 object errmsg;
283 mr.GetValue(typeof(string), out errmsg);
285 throw new DbusError(errname.ToString() + ": " + errmsg.ToString());
287 default:
288 throw new Exception("D-Bus response was not a method return or "
289 +"error");
293 bool VxScalar(string query, out object result)
295 Console.WriteLine(" + VxScalar SQL Query: {0}", query);
297 Message call = CreateMethodCall(DbusConnName, DbusObjPath,
298 DbusInterface, "ExecScalar", "s");
300 MessageWriter mw = new MessageWriter(Connection.NativeEndianness);
301 mw.Write(typeof(string), query);
303 call.Body = mw.ToArray();
305 Message reply = bus.SendWithReplyAndBlock(call);
307 switch (reply.Header.MessageType) {
308 case MessageType.MethodReturn:
310 object replysig;
311 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
312 out replysig))
313 throw new Exception("D-Bus reply had no signature");
315 if (replysig == null || replysig.ToString() != "v")
316 throw new Exception("D-Bus reply had invalid signature");
318 MessageReader reader = new MessageReader(reply);
319 reader.GetValue(out result); // This overload processes a variant
321 return true;
323 case MessageType.Error:
325 object errname;
326 if (!reply.Header.Fields.TryGetValue(FieldCode.ErrorName,
327 out errname))
328 throw new Exception("D-Bus error received but no error name "
329 +"given");
331 object errsig;
332 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
333 out errsig) || errsig.ToString() != "s")
334 throw new DbusError(errname.ToString());
336 MessageReader mr = new MessageReader(reply);
338 object errmsg;
339 mr.GetValue(typeof(string), out errmsg);
341 throw new DbusError(errname.ToString() + ": " + errmsg.ToString());
343 default:
344 throw new Exception("D-Bus response was not a method return or "
345 +"error");
349 bool VxRecordset(string query, out VxColumnInfo[] colinfo,
350 out object[][] data, out bool[][] nullity)
352 Console.WriteLine(" + VxReader SQL Query: {0}", query);
354 Message call = CreateMethodCall(DbusConnName, DbusObjPath,
355 DbusInterface, "ExecRecordset", "s");
357 MessageWriter mw = new MessageWriter(Connection.NativeEndianness);
358 mw.Write(typeof(string), query);
360 call.Body = mw.ToArray();
362 Message reply = bus.SendWithReplyAndBlock(call);
364 switch (reply.Header.MessageType) {
365 case MessageType.MethodReturn:
367 object replysig;
368 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
369 out replysig))
370 throw new Exception("D-Bus reply had no signature");
372 if (replysig == null || replysig.ToString() != "a(issnny)vaay")
373 throw new Exception("D-Bus reply had invalid signature");
375 MessageReader reader = new MessageReader(reply);
376 Array ci;
377 reader.GetValue(typeof(VxColumnInfo[]), out ci);
378 colinfo = (VxColumnInfo[])ci;
380 Signature sig;
381 reader.GetValue(out sig);
383 // TODO: Check that sig matches colinfo
384 // Sig should be of the form a(...)
386 int arraysz;
387 reader.GetValue(out arraysz);
389 // The header is 8-byte aligned
390 reader.ReadPad(8);
391 int endpos = reader.Position + arraysz;;
393 List<object[]> results = new List<object[]>();
394 while (reader.Position < endpos) {
395 object[] row = new object[colinfo.Length];
397 // Each structure element is 8-byte aligned
398 reader.ReadPad(8);
400 for (int i=0; i < row.Length; i++) {
401 switch (colinfo[i].VxColumnType) {
402 case VxColumnType.Int64:
404 Console.WriteLine("Reading Int64 from pos {0}",
405 reader.Position);
406 long cell;
407 reader.GetValue(out cell);
408 row[i] = cell;
409 break;
411 case VxColumnType.Int32:
413 Console.WriteLine("Reading Int32 from pos {0}",
414 reader.Position);
415 int cell;
416 reader.GetValue(out cell);
417 row[i] = cell;
418 break;
420 case VxColumnType.Int16:
422 Console.WriteLine("Reading Int16 from pos {0}",
423 reader.Position);
424 short cell;
425 reader.GetValue(out cell);
426 row[i] = cell;
427 break;
429 case VxColumnType.UInt8:
431 Console.WriteLine("Reading UInt8 from pos {0}",
432 reader.Position);
433 byte cell;
434 reader.GetValue(out cell);
435 row[i] = cell;
436 break;
438 case VxColumnType.Bool:
440 Console.WriteLine("Reading Bool from pos {0}",
441 reader.Position);
442 bool cell;
443 reader.GetValue(out cell);
444 row[i] = cell;
445 break;
447 case VxColumnType.Double:
449 Console.WriteLine("Reading Double from pos {0}",
450 reader.Position);
451 double cell;
452 reader.GetValue(out cell);
453 row[i] = cell;
454 break;
456 case VxColumnType.Uuid:
458 Console.WriteLine("Reading UUID from pos {0}",
459 reader.Position);
460 string cell;
461 reader.GetValue(out cell);
463 if (cell == "") {
464 row[i] = new Guid();
465 } else {
466 row[i] = new Guid(cell);
468 break;
470 case VxColumnType.Binary:
472 Console.WriteLine("Reading Binary from pos {0}",
473 reader.Position);
474 object cell;
475 reader.GetValue(typeof(byte[]), out cell);
476 row[i] = cell;
477 break;
479 case VxColumnType.String:
481 Console.WriteLine("Reading string from pos {0}",
482 reader.Position);
483 string cell;
484 reader.GetValue(out cell);
485 row[i] = cell;
486 break;
488 case VxColumnType.DateTime:
490 Console.WriteLine("Reading DateTime from pos {0}",
491 reader.Position);
492 long seconds;
493 int microseconds;
495 reader.ReadPad(8);
496 reader.GetValue(out seconds);
497 reader.GetValue(out microseconds);
499 VxDbusDateTime dt = new VxDbusDateTime();
500 dt.Seconds = seconds;
501 dt.Microseconds = microseconds;
503 row[i] = dt;
504 break;
506 case VxColumnType.Decimal:
508 Console.WriteLine("Reading Decimal from pos {0}",
509 reader.Position);
510 string cell;
511 reader.GetValue(out cell);
513 if (cell == "") {
514 row[i] = new Decimal();
515 } else {
516 row[i] = Decimal.Parse(cell);
518 break;
520 default:
521 throw new Exception("Invalid column type received");
525 results.Add(row);
528 WVPASSEQ(reader.Position, endpos);
529 if (reader.Position != endpos)
530 throw new Exception("Position mismatch after reading data");
532 data = results.ToArray();
534 object rawnulls;
535 reader.GetValue(typeof(byte[][]), out rawnulls);
537 byte[][] rawnulls_typed = (byte[][])rawnulls;
539 nullity = new bool[rawnulls_typed.Length][];
541 for (int i=0; i < rawnulls_typed.Length; i++) {
542 nullity[i] = new bool[rawnulls_typed[i].Length];
544 for (int j=0; j < rawnulls_typed[i].Length; j++) {
545 nullity[i][j] = (rawnulls_typed[i][j] == 0) ? false : true;
549 return true;
551 case MessageType.Error:
553 object errname;
554 if (!reply.Header.Fields.TryGetValue(FieldCode.ErrorName,
555 out errname))
556 throw new Exception("D-Bus error received but no error name "
557 +"given");
559 object errsig;
560 if (!reply.Header.Fields.TryGetValue(FieldCode.Signature,
561 out errsig) || errsig.ToString() != "s")
562 throw new DbusError(errname.ToString());
564 MessageReader mr = new MessageReader(reply);
566 object errmsg;
567 mr.GetValue(typeof(string), out errmsg);
569 throw new DbusError(errname.ToString() + ": " + errmsg.ToString());
571 default:
572 throw new Exception("D-Bus response was not a method return or "
573 +"error");
577 bool Insert(string table, params object [] param)
579 Console.WriteLine(" + Insert to {0} ({1})", table, String.Join(", ",
580 wv.stringify(param)));
582 System.Text.StringBuilder query = new System.Text.StringBuilder();
583 query.AppendFormat("INSERT INTO [{0}] VALUES (",
584 table.Replace("]","]]"));
586 using (SqlCommand insCmd = con.CreateCommand()) {
587 for (int i=0; i < param.Length; i++) {
588 if (i > 0)
589 query.Append(", ");
591 if (param[i] is DBNull) {
592 query.Append("NULL");
593 } else {
594 string paramName = string.Format("@col{0}", i);
596 query.Append(paramName);
597 insCmd.Parameters.Add(new SqlParameter(paramName,
598 param[i]));
602 query.Append(")");
603 insCmd.CommandText = query.ToString();
605 insCmd.ExecuteNonQuery();
608 return true;
611 string read_lipsum()
613 WVASSERT(File.Exists(lipsum_file));
615 using (StreamReader sr = new StreamReader(lipsum_file)) {
616 return sr.ReadToEnd();
620 string read_unicode()
622 WVASSERT(File.Exists(unicode_file));
624 using (StreamReader sr = new StreamReader(unicode_file)) {
625 return sr.ReadToEnd();
629 Byte [] read_goop()
631 WVASSERT(File.Exists(goop_file));
633 using (FileStream f = new FileStream(goop_file, FileMode.Open,
634 FileAccess.Read))
635 using (BinaryReader sr = new BinaryReader(f)) {
636 return sr.ReadBytes((int)Math.Min(f.Length, Int32.MaxValue));
640 Byte [] read_image()
642 WVASSERT(File.Exists(image_file));
644 using (FileStream f = new FileStream(image_file, FileMode.Open,
645 FileAccess.Read))
646 using (BinaryReader sr = new BinaryReader(f)) {
647 return sr.ReadBytes((int)Math.Min(f.Length, Int32.MaxValue));
651 long GetInt64(SqlDataReader reader, int colnum) {
652 // For some reason, it won't just up-convert int32 to int64
653 if (reader.GetFieldType(colnum) == typeof(System.Int32)) {
654 return reader.GetInt32(colnum);
655 } else if (reader.GetFieldType(colnum) == typeof(System.Int64)) {
656 return reader.GetInt64(colnum);
657 } else if (reader.GetFieldType(colnum) == typeof(System.Decimal)) {
658 return (long)reader.GetDecimal(colnum);
659 } else {
660 // Unknown type
661 bool unknown_type_in_result = true;
662 WVFAIL(unknown_type_in_result);
664 return -1;
668 [Test, Category("Data"), Category("Sanity")]
669 public void EmptyTable()
671 // Check that an empty table is read ok
672 try { VxExec("DROP TABLE test1"); } catch {}
674 try {
675 WVASSERT(VxExec("CREATE TABLE test1 (testcol int not null)"));
677 object result;
678 WVASSERT(VxScalar("SELECT COUNT(*) FROM test1", out result));
679 WVPASSEQ((int)result, 0);
681 VxColumnInfo[] colinfo;
682 object[][] data;
683 bool[][] nullity;
684 WVASSERT(VxRecordset("SELECT * FROM test1", out colinfo, out data,
685 out nullity));
687 WVPASSEQ(colinfo.Length, 1);
688 WVPASSEQ(colinfo[0].ColumnName, "testcol");
689 WVPASSEQ(colinfo[0].ColumnType.ToLowerInvariant(), "int32");
691 WVPASSEQ(data.Length, 0);
692 WVPASSEQ(nullity.Length, 0);
693 } finally {
694 try { VxExec("DROP TABLE test1"); } catch {}
698 [Test, Category("Running"), Category("Errors")]
699 public void NonexistantTable()
701 // Check that a nonexistant table throws an error
702 try {
703 VxColumnInfo[] colinfo;
704 object[][] data;
705 bool[][] nullity;
706 WVEXCEPT(VxRecordset("SELECT * FROM #nonexistant", out colinfo,
707 out data, out nullity));
708 } catch (Wv.Test.WvAssertionFailure e) {
709 throw e;
710 } catch (System.Exception e) {
711 // FIXME: This should check for a vx.db.sqlerror
712 // rather than any dbus error
713 WVPASS(e is DbusError);
716 // The only way to get here is for the test to pass (otherwise an
717 // exception has been generated somewhere), as WVEXCEPT() always throws
718 // something.
721 [Test, Category("Schema"), Category("Sanity")]
722 public void ColumnTypes()
724 // Check that column types are copied correctly to the output table
725 try { VxExec("DROP TABLE test1"); } catch {}
727 string[] colTypes = {
728 // Pulled from the SQL Server management gui app's dropdown list in
729 // the table design screen
730 "bigint", "binary(50)", "bit", "char(10)", "datetime",
731 "decimal(18, 0)", "float", "image", "int", "money", "nchar(10)",
732 "ntext", "numeric(18, 0)", "nvarchar(50)", "nvarchar(MAX)", "real",
733 "smalldatetime", "smallint", "smallmoney", "text",
734 "timestamp", "tinyint", "uniqueidentifier", "varbinary(50)",
735 "varbinary(MAX)", "varchar(50)", "varchar(MAX)", "xml",
736 // "sql_variant", // this is problematic, so it is unsupported
738 // Plus a few more to mix up the parameters a bit, and providing
739 // edge cases
740 "numeric(1, 0)", "numeric(38, 38)", "numeric(1, 1)",
741 "numeric(38, 0)", "nvarchar(4000)", "nvarchar(1)",
742 "varchar(8000)", "varchar(1)", "char(1)", "char(8000)",
743 "nchar(1)", "nchar(4000)", "decimal(1, 0)", "decimal(38, 38)",
744 "decimal(1, 1)", "decimal(38, 0)", "binary(1)", "binary(8000)"
747 foreach (String colType in colTypes) {
748 WVASSERT(VxExec(string.Format("CREATE TABLE test1 (testcol {0})",
749 colType)));
750 // This makes sure it runs the prepare statement
751 WVASSERT(Insert("test1", DBNull.Value));
753 SqlDataReader reader;
754 DataTable schema;
756 WVASSERT(Reader("SELECT * FROM test1", out reader));
757 using (reader)
758 schema = reader.GetSchemaTable();
760 VxColumnInfo[] vxcolinfo;
761 object[][] data;
762 bool[][] nullity;
763 WVASSERT(VxRecordset("SELECT * FROM test1", out vxcolinfo, out data,
764 out nullity));
766 WVPASSEQ(schema.Rows.Count, vxcolinfo.Length);
768 try {
769 for (int colNum = 0; colNum < schema.Rows.Count; colNum++) {
770 DataRow colInfo = schema.Rows[colNum];
772 WVPASSEQ((string)colInfo["ColumnName"],
773 vxcolinfo[colNum].ColumnName);
774 WVPASSEQ((int)colInfo["ColumnOrdinal"], colNum);
775 // FIXME: There must be *some* way to turn this into a
776 // switch...
777 Type type = (Type)colInfo["DataType"];
778 string vxtype = vxcolinfo[colNum].ColumnType.ToLowerInvariant();
779 if (type == typeof(Int64)) {
780 WVPASSEQ(vxtype, "int64");
781 } else if (type == typeof(Int32)) {
782 WVPASSEQ(vxtype, "int32");
783 } else if (type == typeof(Int16)) {
784 WVPASSEQ(vxtype, "int16");
785 } else if (type == typeof(Byte)) {
786 WVPASSEQ(vxtype, "uint8");
787 } else if (type == typeof(Boolean)) {
788 WVPASSEQ(vxtype, "bool");
789 } else if (type == typeof(Single) || type == typeof(Double)) {
790 WVPASSEQ(vxtype, "double");
791 } else if (type == typeof(Guid)) {
792 WVPASSEQ(vxtype, "uuid");
793 } else if (type == typeof(Byte[])) {
794 WVPASSEQ(vxtype, "binary");
795 } else if (type == typeof(string)) {
796 WVPASSEQ(vxtype, "string");
797 } else if (type == typeof(DateTime)) {
798 WVPASSEQ(vxtype, "datetime");
799 } else if (type == typeof(Decimal)) {
800 WVPASSEQ(vxtype, "decimal");
801 } else {
802 bool return_column_type_is_known = false;
803 WVASSERT(return_column_type_is_known);
806 WVPASSEQ((int)colInfo["ColumnSize"],
807 vxcolinfo[colNum].Size);
808 // These next two may have problems with mono vs microsoft
809 // differences
810 WVPASSEQ((short)colInfo["NumericPrecision"],
811 vxcolinfo[colNum].Precision);
812 WVPASSEQ((short)colInfo["NumericScale"],
813 vxcolinfo[colNum].Scale);
815 } finally {
816 try { VxExec("DROP TABLE test1"); } catch {}
821 [Test, Category("Schema"), Category("Errors")]
822 public void EmptyColumnName()
824 // Check that a query with a missing column name is ok
825 SqlDataReader reader;
826 WVASSERT(Reader("SELECT 1", out reader));
827 using (reader)
828 using (DataTable schema = reader.GetSchemaTable()) {
829 WVPASSEQ(reader.FieldCount, 1);
830 WVPASSEQ(schema.Rows.Count, 1);
832 DataRow schemaRow = schema.Rows[0];
833 WVPASSEQ((string)schemaRow["ColumnName"], "");
835 WVPASS(reader.Read());
836 WVPASSEQ((int)reader[0], 1);
838 WVFAIL(reader.Read());
839 WVFAIL(reader.NextResult());
843 [Test, Category("Data")]
844 public void RowOrdering()
846 // Make sure that data comes out in the right order when ordering is
847 // requested from Versaplex
849 // If these are all prime then the permutation is guaranteed to work
850 // without any duplicates (I think it actually works as long as numElems
851 // is coprime with the other two, but making them all prime is safe)
852 const int numElems = 101;
853 const int prime1 = 47;
854 const int prime2 = 53;
856 WVASSERT(Exec("CREATE TABLE #test1 (seq int NOT NULL, "
857 + "num int NOT NULL)"));
859 // j will be a permutation of 0..numElems without resorting to random
860 // numbers, while making sure that we're not inserting in sorted order.
861 for (int i=0, j=0; i < numElems; i++, j = (i*prime1) % numElems) {
862 // This inserts 0..numElems into seq (in a permuted order), with
863 // 0..numElems in num, but permuted in a different order.
864 Insert("#test1", j, (j*prime2) % numElems);
867 SqlDataReader reader;
868 WVASSERT(Reader("SELECT num FROM #test1 ORDER BY seq",
869 out reader));
871 using (reader) {
872 for (int i=0; i < numElems; i++) {
873 WVASSERT(reader.Read());
874 WVPASSEQ((int)reader["num"], (i*prime2) % numElems);
877 WVFAIL(reader.Read());
880 WVASSERT(Exec("DROP TABLE #test1"));
883 [Test, Category("Schema")]
884 public void ColumnOrdering()
886 // Make a bunch of columns and check that they come back in the right
887 // order
889 // For an explanation about the permutation stuff here, see the
890 // RowOrdering test, above
891 const int numCols = 101;
892 const int numSelected = 83;
893 const int prime1 = 47;
894 const int prime2 = 53;
896 System.Text.StringBuilder query = new System.Text.StringBuilder(
897 "CREATE TABLE #test1 (");
899 for (int i=0, j=0; i < numCols; i++, j = (i*prime1) % numCols) {
900 if (i > 0)
901 query.Append(", ");
903 query.AppendFormat("col{0} int", j);
906 query.Append(")");
908 WVASSERT(Exec(query.ToString()));
910 query = new System.Text.StringBuilder("SELECT ");
912 // Don't select all of them, in case that makes a difference. But still
913 // select from the entire range (as opposed to the first few), so still
914 // mod by numCols instead of numSelected.
915 for (int i=0, j=0; i < numSelected; i++, j = (i*prime2) % numCols) {
916 if (i > 0)
917 query.Append(", ");
919 query.AppendFormat("col{0}", j);
921 query.Append(" FROM #test1");
923 SqlDataReader reader;
924 WVASSERT(Reader(query.ToString(), out reader));
926 using (reader) {
927 WVPASSEQ(reader.FieldCount, numSelected);
929 for (int i=0; i < numSelected; i++) {
930 WVPASSEQ((string)reader.GetName(i),
931 string.Format("col{0}", (i*prime2) % numCols));
934 WVFAIL(reader.Read());
937 WVASSERT(Exec("DROP TABLE #test1"));
940 [Test, Category("Data")]
941 public void VerifyIntegers()
943 // bigint, int, smallint, tinyint
944 // Insert 6 rows: max, 10, 0, -10, min, nulls (except tinyint is
945 // unsigned so it has 0 again instead of -10)
946 // Then check that they were copied correctly
947 // Assume that the schema of the output table is correct (tested
948 // elsewhere)
950 WVASSERT(Exec("CREATE TABLE #test1 (bi bigint, i int, si smallint, "
951 + "ti tinyint, roworder int not null)"));
953 WVASSERT(Insert("#test1", Int64.MaxValue, Int32.MaxValue,
954 Int16.MaxValue, Byte.MaxValue, 1));
955 WVASSERT(Insert("#test1", 10, 10, 10, 10, 2));
956 WVASSERT(Insert("#test1", 0, 0, 0, 0, 3));
957 WVASSERT(Insert("#test1", -10, -10, -10, 0, 4));
958 WVASSERT(Insert("#test1", Int64.MinValue, Int32.MinValue,
959 Int16.MinValue, Byte.MinValue, 5));
960 WVASSERT(Insert("#test1", DBNull.Value, DBNull.Value, DBNull.Value,
961 DBNull.Value, 6));
963 SqlDataReader reader;
964 WVASSERT(Reader("SELECT bi,i,si,ti FROM #test1 ORDER BY roworder",
965 out reader));
967 using (reader) {
968 WVASSERT(reader.Read());
969 WVPASSEQ(reader.GetInt64(0), Int64.MaxValue);
970 WVPASSEQ(reader.GetInt32(1), Int32.MaxValue);
971 WVPASSEQ(reader.GetInt16(2), Int16.MaxValue);
972 WVPASSEQ(reader.GetByte(3), Byte.MaxValue);
974 WVASSERT(reader.Read());
975 WVPASSEQ(reader.GetInt64(0), 10);
976 WVPASSEQ(reader.GetInt32(1), 10);
977 WVPASSEQ(reader.GetInt16(2), 10);
978 WVPASSEQ(reader.GetByte(3), 10);
980 WVASSERT(reader.Read());
981 WVPASSEQ(reader.GetInt64(0), 0);
982 WVPASSEQ(reader.GetInt32(1), 0);
983 WVPASSEQ(reader.GetInt16(2), 0);
984 WVPASSEQ(reader.GetByte(3), 0);
986 WVASSERT(reader.Read());
987 WVPASSEQ(reader.GetInt64(0), -10);
988 WVPASSEQ(reader.GetInt32(1), -10);
989 WVPASSEQ(reader.GetInt16(2), -10);
990 WVPASSEQ(reader.GetByte(3), 0);
992 WVASSERT(reader.Read());
993 WVPASSEQ(reader.GetInt64(0), Int64.MinValue);
994 WVPASSEQ(reader.GetInt32(1), Int32.MinValue);
995 WVPASSEQ(reader.GetInt16(2), Int16.MinValue);
996 WVPASSEQ(reader.GetByte(3), Byte.MinValue);
998 WVASSERT(reader.Read());
999 WVPASS(reader.IsDBNull(0));
1000 WVPASS(reader.IsDBNull(1));
1001 WVPASS(reader.IsDBNull(2));
1002 WVPASS(reader.IsDBNull(3));
1004 WVFAIL(reader.Read());
1007 WVASSERT(Exec("DROP TABLE #test1"));
1010 [Test, Category("Data")]
1011 public void VerifyBinary()
1013 // binary, varbinary (not max)
1015 // This must be sorted
1016 int [] sizes = { 1, 10, 50, 255, 4000, 8000 };
1018 string [] types = { "binary", "varbinary" };
1019 int [] typemax = { 8000, 8000 };
1020 int [] charsize = { 1, 1 };
1021 bool [] varsize = { false, true };
1023 Byte [] binary_goop = read_goop();
1025 WVASSERT(binary_goop.Length >= sizes[sizes.Length-1]);
1027 for (int i=0; i < types.Length; i++) {
1028 for (int j=0; j < sizes.Length && sizes[j] <= typemax[i]; j++) {
1029 WVASSERT(Exec(string.Format("CREATE TABLE #test1 "
1030 + "(data {0}({1}), roworder int not null)",
1031 types[i], sizes[j])));
1033 for (int k=0; k <= j; k++) {
1034 Byte [] data = new byte[sizes[k]];
1035 Array.Copy(binary_goop, data, sizes[k]);
1037 WVASSERT(Insert("#test1", new SqlBinary(data), k));
1040 WVASSERT(Insert("#test1", DBNull.Value, j+1));
1042 SqlDataReader reader;
1043 WVASSERT(Reader("SELECT LEN(data), DATALENGTH(data), data FROM "
1044 + "#test1 ORDER BY roworder",
1045 out reader));
1047 using (reader) {
1048 for (int k=0; k <= j; k++) {
1049 Byte [] data = new byte[sizes[k]];
1050 Array.Copy(binary_goop, data, sizes[k]);
1052 WVASSERT(reader.Read());
1054 int len = sizes[varsize[i] ? k : j];
1055 WVPASSEQ(GetInt64(reader, 0), len);
1057 int datalen = sizes[varsize[i] ? k : j]*charsize[i];
1058 WVPASSEQ(GetInt64(reader, 1), datalen);
1060 WVPASSEQ(reader.GetSqlBinary(2), new SqlBinary(data));
1063 WVASSERT(reader.Read());
1064 WVPASS(reader.IsDBNull(2));
1066 WVFAIL(reader.Read());
1069 WVASSERT(Exec("DROP TABLE #test1"));
1074 [Test, Category("Data")]
1075 public void VerifyBit()
1077 // bit
1078 // Insert 3 rows: true, false, null
1079 // Then check that they were copied correctly
1080 // Assume that the schema of the output table is correct (tested
1081 // elsewhere)
1083 WVASSERT(Exec("CREATE TABLE #test1 (b bit, roworder int not null)"));
1085 WVASSERT(Insert("#test1", true, 1));
1086 WVASSERT(Insert("#test1", false, 2));
1087 WVASSERT(Insert("#test1", DBNull.Value, 3));
1089 SqlDataReader reader;
1090 WVASSERT(Reader("SELECT b FROM #test1 ORDER BY roworder",
1091 out reader));
1093 using (reader) {
1094 WVASSERT(reader.Read());
1095 WVPASSEQ(reader.GetBoolean(0), true);
1097 WVASSERT(reader.Read());
1098 WVPASSEQ(reader.GetBoolean(0), false);
1100 WVASSERT(reader.Read());
1101 WVPASS(reader.IsDBNull(0));
1103 WVFAIL(reader.Read());
1106 WVASSERT(Exec("DROP TABLE #test1"));
1109 // The output of LEN() or DATALENGTH() in MS SQL is an int for most types,
1110 // but is a BigNum for varchar(max), nvarchar(max), and varbinary(max).
1111 // We don't really care, so just do what it takes to get a sensible value.
1112 public int GetDataLength(object data)
1114 return data.GetType() == typeof(Decimal) ?
1115 (int)(Decimal)data : (int)data;
1118 [Test, Category("Data")]
1119 public void VerifyChar()
1121 try { VxExec("DROP TABLE test1"); } catch {}
1122 // char, nchar, varchar (in-row or max), nvarchar (in-row or max),
1123 // text, ntext
1124 // This doesn't try to use any non-ascii characters. There is a separate
1125 // test for that.
1127 // This must be sorted
1128 int [] sizes = { 1, 10, 50, 255, 4000, 8000, 8040, 8192, 16080, 16384,
1129 24120, 32160, 32767, 50157 };
1131 string [] types = { "char", "varchar", "nchar", "nvarchar", "text",
1132 "ntext", "varchar(max)", "nvarchar(max)" };
1133 int [] typemax = { 8000, 8000, 4000, 4000, Int32.MaxValue,
1134 Int32.MaxValue/2, Int32.MaxValue, Int32.MaxValue/2 };
1135 int [] charsize = { 1, 1, 2, 2, 1, 2, 1, 2 };
1136 bool [] varsize = { false, true, false, true, true, true, true, true };
1137 bool [] sizeparam = { true, true, true, true, false, false, false,
1138 false };
1139 bool [] lenok = { true, true, true, true, false, false, true, true };
1141 string lipsum_text = read_lipsum();
1143 WVASSERT(lipsum_text.Length >= sizes[sizes.Length-1]);
1145 // FIXME: For any values past the first 4 in each of these arrays,
1146 // dbus-sharp chokes with a "Read length mismatch" exception. It's
1147 // probably related to the packets being longer than usual. See
1148 // GoogleCode bug #1.
1149 for (int i=0; i < types.Length; i++) {
1150 for (int j=0; j < sizes.Length && sizes[j] <= typemax[i]; j++) {
1151 if (sizeparam[i]) {
1152 WVASSERT(VxExec(string.Format("CREATE TABLE test1 "
1153 + "(data {0}({1}), roworder int not null)",
1154 types[i], sizes[j])));
1155 } else {
1156 WVASSERT(VxExec(string.Format("CREATE TABLE test1 "
1157 + "(data {0}, roworder int not null)",
1158 types[i])));
1159 j = sizes.Length-1;
1162 for (int k=0; k <= j; k++) {
1163 WVASSERT(VxExec(string.Format(
1164 "INSERT INTO test1 VALUES ('{0}', {1})",
1165 lipsum_text.Substring(0,
1166 sizes[k]).Replace("'", "''"), k)));
1167 /* This doesn't work because it truncates to 4000 chars
1168 * regardless of if it's a nchar/nvarchar or plain
1169 * char/varchar.
1170 WVASSERT(Insert("test1",
1171 new SqlString(
1172 lipsum_text.Substring(0, sizes[k])), k));
1176 WVASSERT(Insert("test1", DBNull.Value, j+1));
1178 VxColumnInfo[] colinfo;
1179 object[][] data;
1180 bool[][] nullity;
1182 if (lenok[i]) {
1183 WVASSERT(VxRecordset("SELECT LEN(data), DATALENGTH(data), "
1184 +" data FROM test1 ORDER BY roworder",
1185 out colinfo, out data, out nullity));
1186 } else {
1187 WVASSERT(VxRecordset("SELECT -1, "
1188 + "DATALENGTH(data), data FROM test1 "
1189 + "ORDER BY roworder",
1190 out colinfo, out data, out nullity));
1193 WVPASSEQ(data.Length, j+2);
1195 for (int k=0; k <= j; k++) {
1196 if (lenok[i])
1197 WVPASSEQ(GetDataLength(data[k][0]), sizes[k]);
1199 WVPASSEQ(GetDataLength(data[k][1]),
1200 sizes[varsize[i] ? k : j]*charsize[i]);
1201 WVPASSEQ(((string)data[k][2]).Substring(0, sizes[k]),
1202 lipsum_text.Substring(0, sizes[k]));
1205 WVPASS(nullity[j+1][2]);
1207 WVASSERT(Exec("DROP TABLE test1"));
1212 [Test, Category("Data")]
1213 public void VerifyDateTime()
1215 // datetime, smalldatetime
1216 // Insert 7 rows: max, a date in the future, now, a date in the past,
1217 // datetime epoch, min, null
1219 // Then check that they were copied correctly
1220 // Assume that the schema of the output table is correct (tested
1221 // elsewhere)
1223 // The actual dates don't really matter, but:
1224 // - The date in the past is adewhurst's birthday (approximately, PST)
1225 // - The date in the future is 1 second past the signed 32-bit overflow
1226 // of seconds since the unix epoch (UTC)
1227 // - The datetime epoch is January 1 1900 at midnight
1229 // Other notes:
1230 // - The min/max values of SqlDateTime are supposed to correspond to the
1231 // min/max values of the SQL Server datetime type, except Mono doesn't
1232 // quite have the semantics right, so the min/max values are
1233 // hard-coded in instead. Bug filed with Mono.
1234 // - All smalldatetime values are rounded down to the nearest minute,
1235 // since it only has per-minute granularity
1237 SqlDateTime epoch = new SqlDateTime(0, 0);
1238 SqlDateTime smallMin = epoch;
1239 SqlDateTime smallMax = new SqlDateTime(2079, 6, 6, 23, 59, 0, 0);
1241 SqlDateTime dtMin = new SqlDateTime(1753, 1, 1, 0, 0, 0, 0);
1242 // This is wrong, but mono seems to have trouble with the fractional
1243 // parts.
1244 SqlDateTime dtMax = new SqlDateTime(9999, 12, 31, 23, 59, 59, 0);
1246 SqlDateTime pastDate = new SqlDateTime(1984, 12, 2, 3, 0, 0, 0);
1247 SqlDateTime pastDateSmall = new SqlDateTime(1984, 12, 2, 3, 0, 0, 0);
1248 SqlDateTime futureDate = new SqlDateTime(2038, 6, 19, 3, 14, 8, 0);
1249 SqlDateTime futureDateSmall = new SqlDateTime(2038, 6, 19, 3, 14, 0, 0);
1251 // Mono has difficulties converting DateTime to SqlDateTime directly, so
1252 // take it down to per-second precision, which works reliably
1253 // Bug filed with Mono.
1254 DateTime now = DateTime.Now;
1255 SqlDateTime sqlNow = new SqlDateTime(now.Year, now.Month, now.Day,
1256 now.Hour, now.Minute, now.Second);
1257 SqlDateTime sqlNowSmall = new SqlDateTime(now.Year, now.Month, now.Day,
1258 now.Hour, now.Minute, 0);
1260 WVASSERT(Exec("CREATE TABLE #test1 (dt datetime, sdt smalldatetime, "
1261 + "roworder int not null)"));
1263 WVASSERT(Insert("#test1", dtMin, smallMin, 1));
1264 WVASSERT(Insert("#test1", epoch, epoch, 2));
1265 WVASSERT(Insert("#test1", pastDate, pastDateSmall, 3));
1266 WVASSERT(Insert("#test1", sqlNow, sqlNowSmall, 4));
1267 WVASSERT(Insert("#test1", futureDate, futureDateSmall, 5));
1268 WVASSERT(Insert("#test1", dtMax, smallMax, 6));
1269 WVASSERT(Insert("#test1", DBNull.Value, DBNull.Value, 7));
1271 SqlDataReader reader;
1272 WVASSERT(Reader("SELECT dt, sdt FROM #test1 ORDER BY roworder",
1273 out reader));
1275 using (reader) {
1276 WVASSERT(reader.Read());
1277 WVPASSEQ(reader.GetSqlDateTime(0), dtMin);
1278 WVPASSEQ(reader.GetSqlDateTime(1), smallMin);
1280 WVASSERT(reader.Read());
1281 WVPASSEQ(reader.GetSqlDateTime(0), epoch);
1282 WVPASSEQ(reader.GetSqlDateTime(1), epoch);
1284 WVASSERT(reader.Read());
1285 WVPASSEQ(reader.GetSqlDateTime(0), pastDate);
1286 WVPASSEQ(reader.GetSqlDateTime(1), pastDateSmall);
1288 WVASSERT(reader.Read());
1289 WVPASSEQ(reader.GetSqlDateTime(0), sqlNow);
1290 WVPASSEQ(reader.GetSqlDateTime(1), sqlNowSmall);
1292 WVASSERT(reader.Read());
1293 WVPASSEQ(reader.GetSqlDateTime(0), futureDate);
1294 WVPASSEQ(reader.GetSqlDateTime(1), futureDateSmall);
1296 WVASSERT(reader.Read());
1297 WVPASSEQ(reader.GetSqlDateTime(0), dtMax);
1298 WVPASSEQ(reader.GetSqlDateTime(1), smallMax);
1300 WVASSERT(reader.Read());
1301 WVPASS(reader.IsDBNull(0));
1302 WVPASS(reader.IsDBNull(1));
1304 WVFAIL(reader.Read());
1307 WVASSERT(Exec("DROP TABLE #test1"));
1310 [Test, Category("Data")]
1311 public void VerifyDecimal()
1313 // decimal(38,0), decimal(38,38), decimal(18,0), decimal(1,0),
1314 // decimal(1,1), numeric as same types
1315 // Insert 6 rows: max, something positive, 0, something negative, min,
1316 // nulls
1317 // Then check that they were copied correctly
1318 // Assume that the schema of the output table is correct (tested
1319 // elsewhere)
1321 Byte [,] sizes = {
1322 // {precision, scale}
1323 {38, 0},
1324 {38, 38},
1325 {18, 0},
1326 {1, 0},
1327 {1, 1}
1330 // Construct all of the things we will insert
1331 // These are all strings because attempting to use the SqlDecimal class
1332 // just leads to no end of problems. Even Microsoft's .NET
1333 // implementation seems to have issues with the max/min value ones.
1334 object [,] values = {
1336 "99999999999999999999999999999999999999",
1337 "0.99999999999999999999999999999999999999",
1338 "999999999999999999",
1339 "9",
1340 "0.9"
1341 }, {
1342 "123456",
1343 "0.12345600000000000000000000000000000000",
1344 "123456",
1345 "1",
1346 "0.1"
1347 }, {
1349 * The "zero" data set actually makes Mono's TDS library croak.
1350 * But that's not a Versaplex bug. The other data sets should
1351 * give reasonable confidence in Versaplex anyway.
1352 * Bug filed with Mono.
1353 "0",
1354 "0.00000000000000000000000000000000000000",
1355 "0",
1356 "0",
1357 "0.0"
1358 }, {
1360 "-654321",
1361 "-0.65432100000000000000000000000000000000",
1362 "-654321",
1363 "-1",
1364 "-0.1"
1365 }, {
1366 "-99999999999999999999999999999999999999",
1367 "-0.99999999999999999999999999999999999999",
1368 "-999999999999999999",
1369 "-9",
1370 "-0.9"
1371 }, {
1372 DBNull.Value,
1373 DBNull.Value,
1374 DBNull.Value,
1375 DBNull.Value,
1376 DBNull.Value
1380 // Make sure that the data is specified correctly here
1381 WVPASSEQ(sizes.GetLength(0), values.GetLength(1));
1383 // Make the table we're going to create
1384 System.Text.StringBuilder schema = new System.Text.StringBuilder(
1385 "CREATE TABLE #test1 (");
1387 // Make one of each decimal and numeric column. These are in fact
1388 // identical, but since either may show up in real-world tables, testing
1389 // both is a good plan
1390 for (int i=0; i < sizes.GetLength(0); i++) {
1391 schema.AppendFormat("d{0}_{1} decimal({0},{1}), "
1392 + "n{0}_{1} numeric({0},{1}), ", sizes[i,0], sizes[i,1]);
1395 schema.Append("roworder int not null)");
1397 WVASSERT(Exec(schema.ToString()));
1399 // Now insert them
1400 object [] insertParams = new object[2*values.GetLength(1)+1];
1402 for (int i=0; i < values.GetLength(0); i++) {
1403 insertParams[insertParams.Length-1] = i;
1404 for (int j=0; j < insertParams.Length-1; j++) {
1405 insertParams[j] = values[i,j/2];
1407 WVASSERT(Insert("#test1", insertParams));
1410 SqlDataReader reader;
1411 WVASSERT(Reader("SELECT * FROM #test1 ORDER BY roworder",
1412 out reader));
1414 using (reader) {
1415 for (int i=0; i < values.GetLength(0); i++) {
1416 WVASSERT(reader.Read());
1418 for (int j=0; j < insertParams.Length-1; j++) {
1419 if (values[i,j/2] is DBNull) {
1420 WVPASS(reader.IsDBNull(j));
1421 } else {
1422 // The preprocessor doesn't like the comma in the array
1423 // subscripts
1424 string val = (string)values[i,j/2];
1425 string fromdb = reader.GetSqlDecimal(j).ToString();
1427 // Mono produces ".1" and "-.1"
1428 // Microsoft .NET produces "0.1" and "-0.1"
1429 // Deal with that here.
1430 // Bug filed with Mono.
1431 if (val[0] == '0' && fromdb[0] == '.') {
1432 WVPASSEQ(fromdb, val.Substring(1));
1433 } else if (val[0] == '-' && val[1] == '0'
1434 && fromdb[0] == '-' && fromdb[1] == '.') {
1435 WVPASSEQ(fromdb, "-" + val.Substring(2));
1436 } else {
1437 WVPASSEQ(fromdb, val);
1443 WVFAIL(reader.Read());
1446 WVASSERT(Exec("DROP TABLE #test1"));
1449 [Test, Category("Data")]
1450 public void VerifyFloat()
1452 // float(53), float(24), real
1453 // Insert 8 rows: max, something positive, smallest positive, 0,
1454 // smallest negative, something negative, min, nulls
1455 // Then check that they were copied correctly
1456 // Assume that the schema of the output table is correct (tested
1457 // elsewhere)
1459 // Specifically, infinity, -infinity and NaN are not tested here because
1460 // SQL Server appears to reject them as values for float columns
1462 // Construct all of the things we will insert
1463 object [,] values = {
1465 /* Can't use SqlDouble.MaxValue et al. because there are
1466 * rounding issues in Mono somewhere that make it reject the
1467 * exact maximum value. These numbers come from the SQL Server
1468 * 2005 reference for the float data type
1469 * Bug filed with Mono.
1470 SqlDouble.MaxValue,
1471 SqlSingle.MaxValue,
1472 SqlSingle.MaxValue */
1473 1.79E+308d,
1474 3.40E+38f,
1475 3.40E+38f
1476 }, {
1477 /* Mono has problems with sending Math.E in a way that is
1478 * roundtrip-able
1479 * Bug filed with Mono.
1480 (double)Math.E,
1481 (float)Math.E,
1482 (float)Math.E */
1483 2.71828182845905d,
1484 2.718282f,
1485 2.718282f
1486 }, {
1487 /* Can't use Double.Epsilon or Single.Epsilon because SQL server
1488 * complains, even on the Microsoft .NET implementation
1489 * These numbers come from the SQL Server 2005 reference for the
1490 * float data type
1491 Double.Epsilon,
1492 Single.Epsilon,
1493 Single.Epsilon */
1494 2.23E-308d,
1495 1.18E-38f,
1496 1.18E-38f
1497 }, {
1498 0.0d,
1499 0.0f,
1500 0.0f
1501 }, {
1503 -Double.Epsilon,
1504 -Single.Epsilon,
1505 -Single.Epsilon */
1506 -2.23E-308d,
1507 -1.18E-38f,
1508 -1.18E-38f
1509 }, {
1510 -127.001d,
1511 -1270.01f,
1512 -12700.1f
1513 }, {
1515 SqlDouble.MinValue,
1516 SqlSingle.MinValue,
1517 SqlSingle.MinValue */
1518 -1.79E+308d,
1519 -3.40E+38f,
1520 -3.40E+38f
1521 }, {
1522 DBNull.Value,
1523 DBNull.Value,
1524 DBNull.Value
1528 WVASSERT(Exec("CREATE TABLE #test1 (f53 float(53), f24 float(24), "
1529 + "r real, roworder int not null)"));
1531 // Now insert them
1532 object [] insertParams = new object[values.GetLength(1)+1];
1534 for (int i=0; i < values.GetLength(0); i++) {
1535 insertParams[insertParams.Length-1] = i;
1536 for (int j=0; j < insertParams.Length-1; j++) {
1537 insertParams[j] = values[i,j];
1539 WVASSERT(Insert("#test1", insertParams));
1542 SqlDataReader reader;
1543 WVASSERT(Reader("SELECT * FROM #test1 ORDER BY roworder",
1544 out reader));
1546 using (reader) {
1547 for (int i=0; i < values.GetLength(0); i++) {
1548 WVASSERT(reader.Read());
1550 for (int j=0; j < insertParams.Length-1; j++) {
1551 // The preprocessor doesn't like the comma in the array
1552 // subscripts
1553 object val = values[i,j];
1555 if (val is DBNull) {
1556 WVPASS(reader.IsDBNull(j));
1557 } else if (val is double) {
1558 WVPASSEQ(reader.GetDouble(j), (double)val);
1559 } else if (val is float) {
1560 WVPASSEQ(reader.GetFloat(j), (float)val);
1561 } else {
1562 // If we get here, a data type was used in the values
1563 // array that's not handled by one of the above cases
1564 bool test_is_broken = true;
1565 WVFAIL(test_is_broken);
1570 WVFAIL(reader.Read());
1573 WVASSERT(Exec("DROP TABLE #test1"));
1576 [Test, Category("Data")]
1577 public void VerifyMoney()
1579 // money, smallmoney
1580 // Insert 6 rows: max, a positive amount, 0, a negative amount, min,
1581 // null
1582 // Then check that they were copied correctly
1583 // Assume that the schema of the output table is correct (tested
1584 // elsewhere)
1586 WVASSERT(Exec("CREATE TABLE #test1 (m money, sm smallmoney, "
1587 + "roworder int not null)"));
1589 WVASSERT(Insert("#test1", SqlMoney.MaxValue, 214748.3647m, 1));
1590 WVASSERT(Insert("#test1", 1337.42m, 1337.42m, 2));
1591 WVASSERT(Insert("#test1", 0.0m, 0.0m, 3));
1592 WVASSERT(Insert("#test1", -3.141m, -3.141m, 5));
1593 WVASSERT(Insert("#test1", SqlMoney.MinValue, -214748.3648m, 6));
1594 WVASSERT(Insert("#test1", DBNull.Value, DBNull.Value, 7));
1596 SqlDataReader reader;
1597 // Cast the return type because Mono doesn't properly handle negative
1598 // money amounts
1599 // Bug filed with Mono.
1600 WVASSERT(Reader("SELECT CAST(m as decimal(20,4)),"
1601 + "CAST(sm as decimal(20,4)) "
1602 + "FROM #test1 ORDER BY roworder", out reader));
1604 using (reader) {
1605 WVASSERT(reader.Read());
1606 WVPASSEQ(reader.GetDecimal(0), SqlMoney.MaxValue.ToDecimal());
1607 WVPASSEQ(reader.GetDecimal(1), 214748.3647m);
1609 WVASSERT(reader.Read());
1610 WVPASSEQ(reader.GetDecimal(0), 1337.42m);
1611 WVPASSEQ(reader.GetDecimal(1), 1337.42m);
1613 WVASSERT(reader.Read());
1614 WVPASSEQ(reader.GetDecimal(0), 0m);
1615 WVPASSEQ(reader.GetDecimal(1), 0m);
1617 WVASSERT(reader.Read());
1618 WVPASSEQ(reader.GetDecimal(0), -3.141m);
1619 WVPASSEQ(reader.GetDecimal(1), -3.141m);
1621 WVASSERT(reader.Read());
1622 WVPASSEQ(reader.GetDecimal(0), SqlMoney.MinValue.ToDecimal());
1623 WVPASSEQ(reader.GetDecimal(1), -214748.3648m);
1625 WVASSERT(reader.Read());
1626 WVPASS(reader.IsDBNull(0));
1627 WVPASS(reader.IsDBNull(1));
1629 WVFAIL(reader.Read());
1632 WVASSERT(Exec("DROP TABLE #test1"));
1635 [Test, Category("Data")]
1636 public void VerifyTimestamp()
1638 // Create a table with a timestamp column, create a bunch of rows in a
1639 // particular order, then check that they match up after copying
1641 // This permutation strategy is discussed in the RowOrdering test
1642 const int numElems = 101;
1643 const int prime1 = 47;
1645 WVASSERT(Exec("CREATE TABLE #test1 (ts timestamp, "
1646 + "roworder int not null)"));
1648 for (int i=0, j=0; i < numElems; i++, j = (i*prime1) % numElems) {
1649 Insert("#test1", DBNull.Value, j);
1652 SqlDataReader reader;
1653 WVASSERT(Reader("SELECT ts,roworder FROM #test1 ORDER BY roworder",
1654 out reader));
1656 SqlBinary [] tsdata = new SqlBinary[numElems];
1658 using (reader) {
1659 for (int i=0; i < numElems; i++) {
1660 WVASSERT(reader.Read());
1661 WVPASSEQ(reader.GetInt32(1), i);
1662 tsdata[i] = reader.GetSqlBinary(0);
1665 WVFAIL(reader.Read());
1668 WVASSERT(Reader("SELECT ts,roworder FROM #test1 ORDER BY ts",
1669 out reader));
1671 using (reader) {
1672 for (int i=0, j=0; i < numElems; i++, j = (i*prime1) % numElems) {
1673 WVASSERT(reader.Read());
1674 WVPASSEQ(reader.GetInt32(1), j);
1675 WVPASSEQ(reader.GetSqlBinary(0), tsdata[j]);
1678 WVFAIL(reader.Read());
1681 WVASSERT(Exec("DROP TABLE #test1"));
1684 [Test, Category("Data")]
1685 public void VerifyUniqueIdentifier()
1687 // uniqueidentifier
1688 // Insert 2 rows: a valid number, null
1689 // Then check that they were copied correctly
1690 // Assume that the schema of the output table is correct (tested
1691 // elsewhere)
1693 SqlGuid guid = new SqlGuid("6F9619FF-8B86-D011-B42D-00C04FC964FF");
1695 WVASSERT(Exec("CREATE TABLE #test1 (u uniqueidentifier, "
1696 + "roworder int not null)"));
1698 WVASSERT(Insert("#test1", guid, 1));
1699 WVASSERT(Insert("#test1", DBNull.Value, 2));
1701 SqlDataReader reader;
1702 WVASSERT(Reader("SELECT u FROM #test1 ORDER BY roworder",
1703 out reader));
1705 using (reader) {
1706 WVASSERT(reader.Read());
1707 WVPASSEQ(reader.GetSqlGuid(0), guid);
1709 WVASSERT(reader.Read());
1710 WVPASS(reader.IsDBNull(0));
1712 WVFAIL(reader.Read());
1715 WVASSERT(Exec("DROP TABLE #test1"));
1718 [Test, Category("Data")]
1719 public void VerifyVarBinaryMax()
1721 // varbinary(max), image
1723 // This must be sorted
1724 long [] sizes = { 1, 10, 50, 255, 4000, 8000, 8040, 8192, 16080, 16384,
1725 24120, 32160, 32768, 40200, 65536, 131072, 262144, 524288, 1048576,
1726 2097152, 3076506 };
1728 string [] types = { "varbinary(max)", "image" };
1730 Byte [] image_data = read_image();
1732 WVASSERT(image_data.Length >= sizes[sizes.Length-1]);
1734 foreach (string type in types) {
1735 WVASSERT(Exec(string.Format("CREATE TABLE #test1 "
1736 + "(data {0}, roworder int not null)", type)));
1738 for (int k=0; k < sizes.Length; k++) {
1739 Byte [] data = new byte[sizes[k]];
1740 Array.Copy(image_data, data, sizes[k]);
1742 WVASSERT(Insert("#test1", new SqlBinary(data), k));
1745 WVASSERT(Insert("#test1", DBNull.Value, sizes.Length));
1747 SqlDataReader reader;
1748 WVASSERT(Reader("SELECT DATALENGTH(data), data FROM "
1749 + "#test1 ORDER BY roworder",
1750 out reader));
1752 using (reader) {
1753 for (int k=0; k < sizes.Length; k++) {
1754 Byte [] data = new byte[sizes[k]];
1755 Array.Copy(image_data, data, sizes[k]);
1757 WVASSERT(reader.Read());
1759 WVPASSEQ(GetInt64(reader, 0), sizes[k]);
1760 WVPASSEQ(reader.GetSqlBinary(1), new SqlBinary(data));
1763 WVASSERT(reader.Read());
1764 WVPASS(reader.IsDBNull(1));
1766 WVFAIL(reader.Read());
1769 WVASSERT(Exec("DROP TABLE #test1"));
1773 [Test, Category("Data")]
1774 public void VerifyXML()
1776 // xml
1777 // Insert 2 rows: some sample XML, null
1778 // Then check that they were copied correctly
1779 // Assume that the schema of the output table is correct (tested
1780 // elsewhere)
1781 // This isn't very exhaustive, so improvements are welcome.
1782 // This was going to use SqlXml instead of using a string, but Mono
1783 // doesn't support that very well.
1785 // This MUST not have any extra whitespace, as it will be stripped by
1786 // some SQL parser and won't be reproduced when it comes back out.
1787 // This is the style that Microsoft's .NET returns
1788 string xml =
1789 "<outside><!--hi--><element1 />Text<element2 type=\"pretty\" />"
1790 + "</outside>";
1791 // This is the style that Mono returns
1792 string altxml =
1793 "<outside><!--hi--><element1/>Text<element2 type=\"pretty\"/>"
1794 + "</outside>";
1796 WVASSERT(Exec("CREATE TABLE #test1 (x xml, "
1797 + "roworder int not null)"));
1799 WVASSERT(Insert("#test1", xml, 1));
1800 WVASSERT(Insert("#test1", DBNull.Value, 2));
1802 SqlDataReader reader;
1803 WVASSERT(Reader("SELECT x FROM #test1 ORDER BY roworder",
1804 out reader));
1806 using (reader) {
1807 WVASSERT(reader.Read());
1808 // Sigh.
1809 if (reader.GetString(0) == altxml) {
1810 WVPASSEQ(reader.GetString(0), altxml);
1811 } else {
1812 WVPASSEQ(reader.GetString(0), xml);
1815 WVASSERT(reader.Read());
1816 WVPASS(reader.IsDBNull(0));
1818 WVFAIL(reader.Read());
1821 WVASSERT(Exec("DROP TABLE #test1"));
1824 [Test, Category("Data")]
1825 public void Unicode()
1827 // nchar, nvarchar (in-row or max), ntext
1828 // Using lots of non-ascii characters
1830 string unicode_text = read_unicode();
1832 int [] sizes = { 4000, unicode_text.Length };
1833 WVASSERT(unicode_text.Length >= sizes[0]);
1835 string [] types = { "nchar", "nvarchar", "ntext", "nvarchar(max)" };
1836 int [] typemax = { 4000, 4000, Int32.MaxValue/2, Int32.MaxValue/2 };
1837 int [] charsize = { 2, 2, 2, 2 };
1838 bool [] varsize = { false, true, true, true };
1839 bool [] sizeparam = { true, true, false, false };
1840 bool [] lenok = { true, true, false, true };
1842 for (int i=0; i < types.Length; i++) {
1843 for (int j=0; j < sizes.Length && sizes[j] <= typemax[i]; j++) {
1844 if (sizeparam[i]) {
1845 WVASSERT(Exec(string.Format("CREATE TABLE #test1 "
1846 + "(data {0}({1}), roworder int not null)",
1847 types[i], sizes[j])));
1848 } else {
1849 WVASSERT(Exec(string.Format("CREATE TABLE #test1 "
1850 + "(data {0}, roworder int not null)",
1851 types[i])));
1852 j = sizes.Length-1;
1855 for (int k=0; k <= j; k++) {
1856 WVASSERT(Exec(string.Format(
1857 "INSERT INTO #test1 VALUES (N'{0}', {1})",
1858 unicode_text.Substring(0,
1859 sizes[k]).Replace("'", "''"), k)));
1862 SqlDataReader reader;
1864 if (lenok[i]) {
1865 WVASSERT(Reader("SELECT LEN(data), DATALENGTH(data), data "
1866 + "FROM #test1 ORDER BY roworder",
1867 out reader));
1868 } else {
1869 WVASSERT(Reader("SELECT -1, "
1870 + "DATALENGTH(data), data FROM #test1 "
1871 + "ORDER BY roworder",
1872 out reader));
1875 using (reader) {
1876 for (int k=0; k <= j; k++) {
1877 WVASSERT(reader.Read());
1879 if (lenok[i])
1880 WVPASSEQ(GetInt64(reader, 0), sizes[k]);
1882 WVPASSEQ(GetInt64(reader, 1),
1883 sizes[varsize[i] ? k : j]*charsize[i]);
1884 WVPASSEQ(reader.GetString(2).Substring(0, sizes[k]),
1885 unicode_text.Substring(0, sizes[k]));
1888 WVFAIL(reader.Read());
1891 WVASSERT(Exec("DROP TABLE #test1"));
1896 public static void Main()
1898 WvTest.DoMain();