6 using System
.Data
.SqlClient
;
7 using System
.Data
.SqlTypes
;
8 using System
.Collections
.Generic
;
11 using System
.Net
.Sockets
;
16 // Several mono bugs worked around in this test fixture are filed as mono bug
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()
35 public override void Open(AddressEntry entry
)
38 if (entry
.Method
== "unix")
43 if (entry
.Properties
.TryGetValue("path", out path
))
45 else if (entry
.Properties
.TryGetValue("abstract", out path
))
48 throw new Exception("No path specified for UNIX transport");
51 sock
= OpenAbstractUnix(path
);
53 sock
= OpenPathUnix(path
);
55 else if (entry
.Method
== "tcp")
57 string host
= "127.0.0.1";
59 entry
.Properties
.TryGetValue("host", out host
);
60 entry
.Properties
.TryGetValue("port", out port
);
61 sock
= OpenTcp(host
, Int32
.Parse(port
));
64 throw new Exception(String
.Format("Unknown connection method {0}",
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);
80 public Socket
OpenPathUnix(string path
)
82 UnixEndPoint ep
= new UnixEndPoint(path
);
83 Socket client
= new Socket(AddressFamily
.Unix
, SocketType
.Stream
, 0);
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);
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";
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";
118 private const string image_file
= "thtbacs.tiff";
124 public VersaplexTest()
126 // Places to look for the config file.
127 string [] searchfiles
=
130 Path
.Combine("..", "versaplexd.ini")
133 string cfgfile
= null;
134 foreach (string searchfile
in searchfiles
)
135 if (File
.Exists(searchfile
))
136 cfgfile
= searchfile
;
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
];
146 dbname
= cfg
["User Map"]["*"];
148 throw new Exception(String
.Format(
149 "User '{0}' (and '*') missing from config.", uname
));
151 string cfgval
= cfg
["Connections"][dbname
];
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
));
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();
172 bus
= new Bus(trans
);
175 public void Dispose()
188 bool Connect(SqlConnection connection
)
195 bool Exec(string query
)
197 Console
.WriteLine(" + Exec SQL Query: {0}", query
);
199 using (SqlCommand execCmd
= new SqlCommand(query
, con
)) {
200 execCmd
.ExecuteNonQuery();
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();
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();
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
);
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
:
267 case MessageType
.Error
:
270 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.ErrorName
,
272 throw new Exception("D-Bus error received but no error name "
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
);
283 mr
.GetValue(typeof(string), out errmsg
);
285 throw new DbusError(errname
.ToString() + ": " + errmsg
.ToString());
288 throw new Exception("D-Bus response was not a method return or "
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
:
311 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
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
323 case MessageType
.Error
:
326 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.ErrorName
,
328 throw new Exception("D-Bus error received but no error name "
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
);
339 mr
.GetValue(typeof(string), out errmsg
);
341 throw new DbusError(errname
.ToString() + ": " + errmsg
.ToString());
344 throw new Exception("D-Bus response was not a method return or "
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
:
368 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.Signature
,
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
);
377 reader
.GetValue(typeof(VxColumnInfo
[]), out ci
);
378 colinfo
= (VxColumnInfo
[])ci
;
381 reader
.GetValue(out sig
);
383 // TODO: Check that sig matches colinfo
384 // Sig should be of the form a(...)
387 reader
.GetValue(out arraysz
);
389 // The header is 8-byte aligned
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
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}",
407 reader
.GetValue(out cell
);
411 case VxColumnType
.Int32
:
413 Console
.WriteLine("Reading Int32 from pos {0}",
416 reader
.GetValue(out cell
);
420 case VxColumnType
.Int16
:
422 Console
.WriteLine("Reading Int16 from pos {0}",
425 reader
.GetValue(out cell
);
429 case VxColumnType
.UInt8
:
431 Console
.WriteLine("Reading UInt8 from pos {0}",
434 reader
.GetValue(out cell
);
438 case VxColumnType
.Bool
:
440 Console
.WriteLine("Reading Bool from pos {0}",
443 reader
.GetValue(out cell
);
447 case VxColumnType
.Double
:
449 Console
.WriteLine("Reading Double from pos {0}",
452 reader
.GetValue(out cell
);
456 case VxColumnType
.Uuid
:
458 Console
.WriteLine("Reading UUID from pos {0}",
461 reader
.GetValue(out cell
);
466 row
[i
] = new Guid(cell
);
470 case VxColumnType
.Binary
:
472 Console
.WriteLine("Reading Binary from pos {0}",
475 reader
.GetValue(typeof(byte[]), out cell
);
479 case VxColumnType
.String
:
481 Console
.WriteLine("Reading string from pos {0}",
484 reader
.GetValue(out cell
);
488 case VxColumnType
.DateTime
:
490 Console
.WriteLine("Reading DateTime from pos {0}",
496 reader
.GetValue(out seconds
);
497 reader
.GetValue(out microseconds
);
499 VxDbusDateTime dt
= new VxDbusDateTime();
500 dt
.Seconds
= seconds
;
501 dt
.Microseconds
= microseconds
;
506 case VxColumnType
.Decimal
:
508 Console
.WriteLine("Reading Decimal from pos {0}",
511 reader
.GetValue(out cell
);
514 row
[i
] = new Decimal();
516 row
[i
] = Decimal
.Parse(cell
);
521 throw new Exception("Invalid column type received");
528 WVPASSEQ(reader
.Position
, endpos
);
529 if (reader
.Position
!= endpos
)
530 throw new Exception("Position mismatch after reading data");
532 data
= results
.ToArray();
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;
551 case MessageType
.Error
:
554 if (!reply
.Header
.Fields
.TryGetValue(FieldCode
.ErrorName
,
556 throw new Exception("D-Bus error received but no error name "
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
);
567 mr
.GetValue(typeof(string), out errmsg
);
569 throw new DbusError(errname
.ToString() + ": " + errmsg
.ToString());
572 throw new Exception("D-Bus response was not a method return or "
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
++) {
591 if (param
[i
] is DBNull
) {
592 query
.Append("NULL");
594 string paramName
= string.Format("@col{0}", i
);
596 query
.Append(paramName
);
597 insCmd
.Parameters
.Add(new SqlParameter(paramName
,
603 insCmd
.CommandText
= query
.ToString();
605 insCmd
.ExecuteNonQuery();
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();
631 WVASSERT(File
.Exists(goop_file
));
633 using (FileStream f
= new FileStream(goop_file
, FileMode
.Open
,
635 using (BinaryReader sr
= new BinaryReader(f
)) {
636 return sr
.ReadBytes((int)Math
.Min(f
.Length
, Int32
.MaxValue
));
642 WVASSERT(File
.Exists(image_file
));
644 using (FileStream f
= new FileStream(image_file
, FileMode
.Open
,
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
);
661 bool unknown_type_in_result
= true;
662 WVFAIL(unknown_type_in_result
);
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 {}
675 WVASSERT(VxExec("CREATE TABLE test1 (testcol int not null)"));
678 WVASSERT(VxScalar("SELECT COUNT(*) FROM test1", out result
));
679 WVPASSEQ((int)result
, 0);
681 VxColumnInfo
[] colinfo
;
684 WVASSERT(VxRecordset("SELECT * FROM test1", out colinfo
, out data
,
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);
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
703 VxColumnInfo
[] colinfo
;
706 WVEXCEPT(VxRecordset("SELECT * FROM #nonexistant", out colinfo
,
707 out data
, out nullity
));
708 } catch (Wv
.Test
.WvAssertionFailure 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
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
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})",
750 // This makes sure it runs the prepare statement
751 WVASSERT(Insert("test1", DBNull
.Value
));
753 SqlDataReader reader
;
756 WVASSERT(Reader("SELECT * FROM test1", out reader
));
758 schema
= reader
.GetSchemaTable();
760 VxColumnInfo
[] vxcolinfo
;
763 WVASSERT(VxRecordset("SELECT * FROM test1", out vxcolinfo
, out data
,
766 WVPASSEQ(schema
.Rows
.Count
, vxcolinfo
.Length
);
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
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");
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
810 WVPASSEQ((short)colInfo
["NumericPrecision"],
811 vxcolinfo
[colNum
].Precision
);
812 WVPASSEQ((short)colInfo
["NumericScale"],
813 vxcolinfo
[colNum
].Scale
);
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
));
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",
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
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
) {
903 query
.AppendFormat("col{0} int", j
);
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
) {
919 query
.AppendFormat("col{0}", j
);
921 query
.Append(" FROM #test1");
923 SqlDataReader reader
;
924 WVASSERT(Reader(query
.ToString(), out 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
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
,
963 SqlDataReader reader
;
964 WVASSERT(Reader("SELECT bi,i,si,ti FROM #test1 ORDER BY roworder",
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",
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()
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
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",
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),
1124 // This doesn't try to use any non-ascii characters. There is a separate
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,
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
++) {
1152 WVASSERT(VxExec(string.Format("CREATE TABLE test1 "
1153 + "(data {0}({1}), roworder int not null)",
1154 types
[i
], sizes
[j
])));
1156 WVASSERT(VxExec(string.Format("CREATE TABLE test1 "
1157 + "(data {0}, roworder int not null)",
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
1170 WVASSERT(Insert("test1",
1172 lipsum_text.Substring(0, sizes[k])), k));
1176 WVASSERT(Insert("test1", DBNull
.Value
, j
+1));
1178 VxColumnInfo
[] colinfo
;
1183 WVASSERT(VxRecordset("SELECT LEN(data), DATALENGTH(data), "
1184 +" data FROM test1 ORDER BY roworder",
1185 out colinfo
, out data
, out nullity
));
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
++) {
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
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
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
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",
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,
1317 // Then check that they were copied correctly
1318 // Assume that the schema of the output table is correct (tested
1322 // {precision, scale}
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",
1343 "0.12345600000000000000000000000000000000",
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.
1354 "0.00000000000000000000000000000000000000",
1361 "-0.65432100000000000000000000000000000000",
1366 "-99999999999999999999999999999999999999",
1367 "-0.99999999999999999999999999999999999999",
1368 "-999999999999999999",
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()));
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",
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
));
1422 // The preprocessor doesn't like the comma in the array
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));
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
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.
1472 SqlSingle.MaxValue */
1477 /* Mono has problems with sending Math.E in a way that is
1479 * Bug filed with Mono.
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
1517 SqlSingle.MinValue */
1528 WVASSERT(Exec("CREATE TABLE #test1 (f53 float(53), f24 float(24), "
1529 + "r real, roworder int not null)"));
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",
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
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
);
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,
1582 // Then check that they were copied correctly
1583 // Assume that the schema of the output table is correct (tested
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
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
));
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",
1656 SqlBinary
[] tsdata
= new SqlBinary
[numElems
];
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",
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()
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
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",
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,
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",
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()
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
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
1789 "<outside><!--hi--><element1 />Text<element2 type=\"pretty\" />"
1791 // This is the style that Mono returns
1793 "<outside><!--hi--><element1/>Text<element2 type=\"pretty\"/>"
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",
1807 WVASSERT(reader
.Read());
1809 if (reader
.GetString(0) == altxml
) {
1810 WVPASSEQ(reader
.GetString(0), altxml
);
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
++) {
1845 WVASSERT(Exec(string.Format("CREATE TABLE #test1 "
1846 + "(data {0}({1}), roworder int not null)",
1847 types
[i
], sizes
[j
])));
1849 WVASSERT(Exec(string.Format("CREATE TABLE #test1 "
1850 + "(data {0}, roworder int not null)",
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
;
1865 WVASSERT(Reader("SELECT LEN(data), DATALENGTH(data), data "
1866 + "FROM #test1 ORDER BY roworder",
1869 WVASSERT(Reader("SELECT -1, "
1870 + "DATALENGTH(data), data FROM #test1 "
1871 + "ORDER BY roworder",
1876 for (int k
=0; k
<= j
; k
++) {
1877 WVASSERT(reader
.Read());
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()