wvstreams a7889bc...c53e3f1: wvdbusserver: implement NameHasOwner request.
[versaplex.git] / versaplexd / vxapi.cs
blob17095ba026107b55f21f1d618cd30dfc7b7f8ade
1 using System;
2 using System.Linq;
3 using System.Text;
4 using System.Data;
5 using System.Data.Common;
6 using System.Data.SqlClient;
7 using System.Collections.Generic;
8 using Wv;
9 using Wv.Extensions;
11 internal static class VxDb {
12 static WvLog log = new WvLog("VxDb", WvLog.L.Debug2);
14 internal static void ExecScalar(string connid, string query,
15 out object result)
17 log.print(WvLog.L.Debug3, "ExecScalar {0}\n", query);
19 try
21 using (var dbi = VxSqlPool.create(connid))
22 result = dbi.select_one(query).inner;
24 catch (DbException e)
26 throw new VxSqlException(e.Message, e);
31 internal static void SendChunkRecordSignal(Connection conn,
32 Message call, string sender,
33 VxColumnInfo[] colinfo,
34 object[][] data, byte[][] nulls)
36 MessageWriter writer =
37 VxDbInterfaceRouter.PrepareRecordsetWriter(colinfo, data, nulls);
38 writer.Write(typeof(uint), call.serial);
40 Message signal = VxDbus.CreateSignal(sender, "ChunkRecordsetSig",
41 "a(issnny)vaayu",
42 writer);
44 // For debugging
45 VxDbus.MessageDump(" S>> ", signal);
47 conn.Send(signal);
51 internal static void ExecChunkRecordset(Connection conn,
52 Message call, out Message reply)
54 string connid = VxDbInterfaceRouter.GetClientId(call);
56 if (connid == null)
58 reply = VxDbus.CreateError(
59 "org.freedesktop.DBus.Error.Failed",
60 "Could not identify the client", call);
61 return;
64 var it = call.iter();
66 string query = it.pop();
67 string iquery = query.ToLower().Trim();
68 reply = null;
69 // XXX this is fishy, really... whitespace fucks it up.
71 if (iquery.StartsWith("list tables"))
72 query = "exec sp_tables";
73 else if (iquery.StartsWith("list columns "))
74 query = String.Format("exec sp_columns @table_name='{0}'",
75 query.Substring(13));
76 else if (iquery.StartsWith("list all table") &&
77 iquery.StartsWith("list all tablefunction") == false)
78 query = "select distinct cast(Name as varchar(max)) Name"
79 + " from sysobjects "
80 + " where objectproperty(id,'IsTable')=1 "
81 + " and xtype='U' "
82 + " order by Name ";
83 else if (iquery.StartsWith("list all"))
84 // Format: list all {view|trigger|procedure|scalarfunction|tablefunction}
85 // Returns: a list of all of whatever
86 query = String.Format(
87 "select distinct "
88 + " cast (object_name(id) as varchar(256)) Name "
89 + " from syscomments "
90 + " where objectproperty(id,'Is{0}') = 1 "
91 + " order by Name ",
92 query.Split(' ')[2].Trim());
93 else if (iquery.StartsWith("get object"))
94 // Format:
95 // get object {view|trigger|procedure|scalarfunction|tablefunction} name
96 // Returns: the "source code" to the object
97 query = String.Format(
98 "select cast(text as varchar(max)) text "
99 + "from syscomments "
100 + "where objectproperty(id, 'Is{0}') = 1 "
101 + "and object_name(id) = '{1}' "
102 + "order by number, colid ",
103 query.Split(' ')[2].Trim(),
104 query.Split(' ')[3].Trim());
105 else if (iquery.StartsWith("drop") || iquery.StartsWith("create") ||
106 iquery.StartsWith("insert") || iquery.StartsWith("update"))
108 //FIXME: Are the above the only ways to modify a DB, aka the only
109 // cases where we have to call the old ExecRecordSet?
110 //FIXME: This is an ugly way to handle these cases, but it works!
111 VxDbInterfaceRouter.CallExecRecordset(conn, call, out reply);
112 return;
115 log.print(WvLog.L.Debug3, "ExecChunkRecordset {0}\n", query);
117 string sender = call.sender;
121 // FIXME: Sadly, this is stupidly similar to ExecRecordset.
122 // Anything we can do here to identify commonalities?
123 using (var dbi = VxSqlPool.create(connid))
124 using (WvSqlRows resultset = dbi.select(query))
126 List<object[]> rows = new List<object[]>();
127 List<byte[]> rownulls = new List<byte[]>();
129 // Our size here is just an approximation. Start it at 1
130 // to ensure that at least one packet always gets sent.
131 int cursize = 1;
133 var columns = resultset.columns.ToArray();
134 VxColumnInfo[] colinfo = ProcessSchema(columns);
135 int ncols = columns.Count();
137 foreach (WvSqlRow cur_row in resultset)
139 object[] row = new object[ncols];
140 byte[] rownull = new byte[ncols];
141 cursize += rownull.Length;
143 for (int i = 0; i < ncols; i++)
145 WvAutoCast cval = cur_row[i];
146 bool isnull = cval.IsNull;
148 row[i] = null;
150 rownull[i] = isnull ? (byte)1 : (byte)0;
152 switch (colinfo[i].VxColumnType)
154 case VxColumnType.Int64:
155 row[i] = !isnull ?
156 (Int64)cval : new Int64();
157 cursize += sizeof(Int64);
158 break;
159 case VxColumnType.Int32:
160 row[i] = !isnull ?
161 (Int32)cval : new Int32();
162 cursize += sizeof(Int32);
163 break;
164 case VxColumnType.Int16:
165 row[i] = !isnull ?
166 (Int16)cval : new Int16();
167 cursize += sizeof(Int16);
168 break;
169 case VxColumnType.UInt8:
170 row[i] = !isnull ?
171 (Byte)cval : new Byte();
172 cursize += sizeof(Byte);
173 break;
174 case VxColumnType.Bool:
175 row[i] = !isnull ?
176 (bool)cval : new Boolean();
177 cursize += sizeof(Boolean);
178 break;
179 case VxColumnType.Double:
180 // Might return a Single or Double
181 // FIXME: Check if getting a single causes this
182 // to croak
183 row[i] = !isnull ?
184 (double)cval : (double)0.0;
185 cursize += sizeof(double);
186 break;
187 case VxColumnType.Uuid:
188 //FIXME: Do I work?
189 row[i] = !isnull ?
190 (string)cval : "";
191 cursize += !isnull ?
192 ((string)cval).Length * sizeof(Char) : 0;
193 break;
194 case VxColumnType.Binary:
196 if (isnull)
198 row[i] = new byte[0];
199 break;
202 row[i] = (byte[])cval;
203 cursize += ((byte[])cval).Length;
204 break;
206 case VxColumnType.String:
207 row[i] = !isnull ? (string)cval : "";
208 cursize += !isnull ?
209 ((string)cval).Length * sizeof(Char) : 0;
210 break;
211 case VxColumnType.DateTime:
212 row[i] = !isnull ?
213 new VxDbusDateTime((DateTime)cval) :
214 new VxDbusDateTime();
215 cursize += System.Runtime.InteropServices.Marshal.SizeOf((VxDbusDateTime)row[i]);
216 break;
217 case VxColumnType.Decimal:
218 row[i] = !isnull ?
219 ((Decimal)cval).ToString() : "";
220 cursize += ((string)(row[i])).Length *
221 sizeof(Char);
222 break;
224 } // column iterator
226 rows.Add(row);
227 rownulls.Add(rownull);
229 if (cursize >= 1024*1024) //approx 1 MB
231 log.print(WvLog.L.Debug4,
232 "(1 MB reached; {0} rows)\n",
233 rows.Count);
235 SendChunkRecordSignal(conn, call, sender, colinfo,
236 rows.ToArray(),
237 rownulls.ToArray());
239 rows = new List<object[]>();
240 rownulls = new List<byte[]>();
241 cursize = 0;
243 } // row iterator
245 if (cursize > 0)
247 log.print(WvLog.L.Debug4, "(Remaining data; {0} rows)\n",
248 rows.Count);
250 SendChunkRecordSignal(conn, call, sender, colinfo,
251 rows.ToArray(),
252 rownulls.ToArray());
254 } // using
255 MessageWriter replywriter =
256 new MessageWriter(Connection.NativeEndianness);
257 replywriter.Write(typeof(string), "ChunkRecordset sent you all your data!");
258 reply = VxDbus.CreateReply(call, "s", replywriter);
259 } catch (DbException e) {
260 throw new VxSqlException(e.Message, e);
264 internal static void ExecRecordset(string connid, string query,
265 out VxColumnInfo[] colinfo, out object[][] data,
266 out byte[][] nullity)
268 // XXX this is fishy
270 if (query.ToLower().StartsWith("list tables"))
271 query = "exec sp_tables";
272 else if (query.ToLower().StartsWith("list columns "))
273 query = String.Format("exec sp_columns @table_name='{0}'",
274 query.Substring(13));
275 else if (query.ToLower().StartsWith("list all table") &&
276 query.ToLower().StartsWith("list all tablefunction") == false)
277 query = "select distinct cast(Name as varchar(max)) Name"
278 + " from sysobjects "
279 + " where objectproperty(id,'IsTable')=1 "
280 + " and xtype='U' "
281 + " order by Name ";
282 else if (query.ToLower().StartsWith("list all"))
283 // Format: list all {view|trigger|procedure|scalarfunction|tablefunction}
284 // Returns: a list of all of whatever
285 query = String.Format(
286 "select distinct "
287 + " cast (object_name(id) as varchar(256)) Name "
288 + " from syscomments "
289 + " where objectproperty(id,'Is{0}') = 1 "
290 + " order by Name ",
291 query.Split(' ')[2].Trim());
292 else if (query.ToLower().StartsWith("get object"))
293 // Format:
294 // get object {view|trigger|procedure|scalarfunction|tablefunction} name
295 // Returns: the "source code" to the object
296 query = String.Format(
297 "select cast(text as varchar(max)) text "
298 + "from syscomments "
299 + "where objectproperty(id, 'Is{0}') = 1 "
300 + "and object_name(id) = '{1}' "
301 + "order by number, colid ",
302 query.Split(' ')[2].Trim(),
303 query.Split(' ')[3].Trim());
305 log.print(WvLog.L.Debug3, "ExecRecordset {0}\n", query);
307 try {
308 List<object[]> rows = new List<object[]>();
309 List<byte[]> rownulls = new List<byte[]>();
311 using (var dbi = VxSqlPool.create(connid))
312 using (var result = dbi.select(query))
314 var columns = result.columns.ToArray();
316 if (columns.Length <= 0)
317 log.print("No columns in resulting data set.");
319 colinfo = ProcessSchema(result.columns);
321 foreach (var r in result)
323 object[] row = new object[columns.Length];
324 byte[] rownull = new byte[columns.Length];
326 for (int i = 0; i < columns.Length; i++)
328 bool isnull = r[i].IsNull;
329 row[i] = null;
330 rownull[i] = isnull ? (byte)1 : (byte)0;
332 switch (colinfo[i].VxColumnType)
334 case VxColumnType.Int64:
335 row[i] = !isnull ? (Int64)r[i] : new Int64();
336 break;
337 case VxColumnType.Int32:
338 row[i] = !isnull ? (Int32)r[i] : new Int32();
339 break;
340 case VxColumnType.Int16:
341 row[i] = !isnull ? (Int16)r[i] : new Int16();
342 break;
343 case VxColumnType.UInt8:
344 row[i] = !isnull ? (Byte)r[i] : new Byte();
345 break;
346 case VxColumnType.Bool:
347 row[i] = !isnull ? (bool)r[i] : new Boolean();
348 break;
349 case VxColumnType.Double:
350 // Might return a Single or Double
351 // FIXME: Check if getting a single causes this
352 // to croak
353 row[i] = !isnull ? (double)r[i] : (double)0.0;
354 break;
355 case VxColumnType.Uuid:
356 row[i] = !isnull ? ((Guid)r[i]).ToString() : "";
357 break;
358 case VxColumnType.Binary:
359 row[i] = !isnull ? (byte[])r[i] : new byte[0];
360 break;
361 case VxColumnType.String:
362 row[i] = !isnull ? (string)r[i] : "";
363 break;
364 case VxColumnType.DateTime:
365 row[i] = !isnull ?
366 new VxDbusDateTime(r[i]) :
367 new VxDbusDateTime();
368 break;
369 case VxColumnType.Decimal:
370 row[i] = !isnull
371 ? ((decimal)r[i]).ToString() : "";
372 break;
376 rows.Add(row);
377 rownulls.Add(rownull);
380 data = rows.ToArray();
381 nullity = rownulls.ToArray();
382 log.print(WvLog.L.Debug4, "({0} rows)\n", data.Length);
383 wv.assert(nullity.Length == data.Length);
385 } catch (DbException e) {
386 throw new VxSqlException(e.Message, e);
390 private static VxColumnInfo[] ProcessSchema(IEnumerable<WvColInfo> columns)
392 // FIXME: This is stupidly similar to ProcessSchema
393 int ncols = columns.Count();
394 VxColumnInfo[] colinfo = new VxColumnInfo[ncols];
396 if (ncols <= 0)
397 return colinfo;
399 int i = 0;
401 foreach (WvColInfo col in columns)
403 System.Type type = col.type;
405 if (type == typeof(object))
407 // We're not even going to try to handle this yet
408 throw new VxBadSchemaException("Columns of type sql_variant "
409 + "are not supported by Versaplex at this time");
412 VxColumnType coltype;
414 if (type == typeof(Int64)) {
415 coltype = VxColumnType.Int64;
416 } else if (type == typeof(Int32)) {
417 coltype = VxColumnType.Int32;
418 } else if (type == typeof(Int16)) {
419 coltype = VxColumnType.Int16;
420 } else if (type == typeof(Byte)) {
421 coltype = VxColumnType.UInt8;
422 } else if (type == typeof(Boolean)) {
423 coltype = VxColumnType.Bool;
424 } else if (type == typeof(Single) || type == typeof(Double)) {
425 coltype = VxColumnType.Double;
426 } else if (type == typeof(Guid)) {
427 coltype = VxColumnType.Uuid;
428 } else if (type == typeof(Byte[])) {
429 coltype = VxColumnType.Binary;
430 } else if (type == typeof(string)) {
431 coltype = VxColumnType.String;
432 } else if (type == typeof(DateTime)) {
433 coltype = VxColumnType.DateTime;
434 } else if (type == typeof(Decimal)) {
435 coltype = VxColumnType.Decimal;
436 } else {
437 throw new VxBadSchemaException("Columns of type "
438 + type.ToString() + " are not supported by "
439 + "Versaplex at this time " +
440 "(column " + col.name + ")");
443 colinfo[i] = new VxColumnInfo(col.name, coltype,
444 col.nullable, col.size, col.precision, col.scale);
446 i++;
449 return colinfo;
453 public class VxDbInterfaceRouter : VxInterfaceRouter
456 static WvLog log = new WvLog("VxDbInterfaceRouter");
457 static readonly VxDbInterfaceRouter instance;
458 public static VxDbInterfaceRouter Instance {
459 get { return instance; }
462 static VxDbInterfaceRouter() {
463 instance = new VxDbInterfaceRouter();
466 private VxDbInterfaceRouter() : base("vx.db")
468 methods.Add("Test", CallTest);
469 methods.Add("Quit", CallQuit);
470 methods.Add("ExecScalar", CallExecScalar);
471 methods.Add("ExecRecordset", CallExecRecordset);
472 methods.Add("ExecChunkRecordset", CallExecChunkRecordset);
473 methods.Add("GetSchemaChecksums", CallGetSchemaChecksums);
474 methods.Add("GetSchema", CallGetSchema);
475 methods.Add("PutSchema", CallPutSchema);
476 methods.Add("DropSchema", CallDropSchema);
477 methods.Add("GetSchemaData", CallGetSchemaData);
478 methods.Add("PutSchemaData", CallPutSchemaData);
481 protected override void ExecuteCall(MethodCallProcessor processor,
482 Connection conn,
483 Message call, out Message reply)
485 try {
486 processor(conn, call, out reply);
487 } catch (VxRequestException e) {
488 reply = VxDbus.CreateError(e.DBusErrorType, e.Message, call);
489 log.print("SQL result: {0}\n", e.Short());
490 } catch (Exception e) {
491 reply = VxDbus.CreateError(
492 "vx.db.exception",
493 "An internal error occurred.", call);
494 log.print("{0}\n", e.ToString());
498 static Dictionary<string,string> usernames = new Dictionary<string, string>();
500 public static string GetClientId(Message call)
502 string sender = call.sender;
504 // For now, the client ID is just the username of the Unix UID that
505 // DBus has associated with the connection.
506 string username;
507 if (!usernames.TryGetValue(sender, out username))
511 // FIXME: We should be using VersaMain.conn here,
512 // not the session bus!!
513 // FIXME: This will likely change as we find a more
514 // universal way to do SSL authentication via D-Bus.
515 username = VxSqlPool.GetUsernameForCert(
516 Bus.Session.GetCertFingerprint(sender));
518 catch
522 // FIXME: This system call isn't actually standard
523 // FIXME: we should be using VersaMain.conn here,
524 // not the session bus!!
525 username = Bus.Session.GetUnixUserName(sender);
527 catch
531 // FIXME: This system call is standard, but not useful
532 // on Windows.
533 // FIXME: we should be using VersaMain.conn here,
534 // not the session bus!!
535 username = Bus.Session.GetUnixUser(sender).ToString();
537 catch
539 username = "*"; // use default connection, if any
546 // Remember the result, so we don't have to ask DBus all the time
547 usernames[sender] = username;
549 log.print(WvLog.L.Info,
550 "New connection '{0}' is user '{1}'\n",
551 sender, username);
554 return username;
557 private static Message CreateUnknownMethodReply(Message call,
558 string methodname)
560 return VxDbus.CreateError(
561 "org.freedesktop.DBus.Error.UnknownMethod",
562 String.Format(
563 "No overload of {0} has signature '{1}'",
564 methodname, call.Signature), call);
567 private static void CallTest(Connection conn,
568 Message call, out Message reply)
570 if (call.Signature.ToString() != "") {
571 reply = CreateUnknownMethodReply(call, "Test");
572 return;
575 string clientid = GetClientId(call);
576 if (clientid == null)
578 reply = VxDbus.CreateError(
579 "org.freedesktop.DBus.Error.Failed",
580 "Could not identify the client", call);
581 return;
584 VxColumnInfo[] colinfo;
585 object[][] data;
586 byte[][] nullity;
587 VxDb.ExecRecordset(clientid, "select 'Works! :D'",
588 out colinfo, out data, out nullity);
590 // FIXME: Add vx.db.toomuchdata error
591 MessageWriter writer = PrepareRecordsetWriter(colinfo, data, nullity);
593 reply = VxDbus.CreateReply(call, "a(issnny)vaay", writer);
595 // For debugging
596 VxDbus.MessageDump(" >> ", reply);
599 private static void CallQuit(Connection conn,
600 Message call, out Message reply)
602 // FIXME: Check permissions here
603 MessageWriter writer =
604 new MessageWriter(Connection.NativeEndianness);
605 writer.Write(typeof(string), "Quit");
606 reply = VxDbus.CreateReply(call, "s", writer);
607 VersaMain.want_to_die = true;
609 // For debugging
610 VxDbus.MessageDump(" >> ", reply);
613 private static void CallExecScalar(Connection conn,
614 Message call, out Message reply)
616 if (call.Signature.ToString() != "s") {
617 reply = CreateUnknownMethodReply(call, "ExecScalar");
618 return;
621 if (call.Body == null) {
622 reply = VxDbus.CreateError(
623 "org.freedesktop.DBus.Error.InvalidSignature",
624 "Signature provided but no body received", call);
625 return;
628 string clientid = GetClientId(call);
629 if (clientid == null)
631 reply = VxDbus.CreateError(
632 "org.freedesktop.DBus.Error.Failed",
633 "Could not identify the client", call);
634 return;
637 var it = call.iter();
638 string query = it.pop();
640 object result;
641 VxDb.ExecScalar(clientid, (string)query, out result);
643 MessageWriter writer =
644 new MessageWriter(Connection.NativeEndianness);
645 writer.WriteV(result);
647 reply = VxDbus.CreateReply(call, "v", writer);
649 // For debugging
650 VxDbus.MessageDump(" >> ", reply);
653 private static void _WriteColInfo(MessageWriter w,
654 VxColumnInfo[] colinfo)
656 // a(issnny)
657 foreach (VxColumnInfo c in colinfo)
658 c.Write(w);
661 private static void WriteColInfo(MessageWriter writer,
662 VxColumnInfo[] colinfo)
664 writer.WriteDelegatePrependSize(delegate(MessageWriter w)
666 _WriteColInfo(w, colinfo);
667 }, 8);
670 public static void CallExecRecordset(Connection conn,
671 Message call, out Message reply)
673 if (call.Signature.ToString() != "s") {
674 reply = CreateUnknownMethodReply(call, "ExecRecordset");
675 return;
678 if (call.Body == null) {
679 reply = VxDbus.CreateError(
680 "org.freedesktop.DBus.Error.InvalidSignature",
681 "Signature provided but no body received", call);
682 return;
685 string clientid = GetClientId(call);
686 if (clientid == null)
688 reply = VxDbus.CreateError(
689 "org.freedesktop.DBus.Error.Failed",
690 "Could not identify the client", call);
691 return;
694 var it = call.iter();
695 string query = it.pop();
697 VxColumnInfo[] colinfo;
698 object[][] data;
699 byte[][] nullity;
700 VxDb.ExecRecordset(clientid, (string)query,
701 out colinfo, out data, out nullity);
703 // FIXME: Add vx.db.toomuchdata error
704 MessageWriter writer = PrepareRecordsetWriter(colinfo, data, nullity);
706 reply = VxDbus.CreateReply(call, "a(issnny)vaay", writer);
708 // For debugging
709 VxDbus.MessageDump(" >> ", reply);
712 private static void CallExecChunkRecordset(Connection conn,
713 Message call, out Message reply)
715 // XXX: Stuff in this comment block shamelessly stolen from
716 // "CallExecRecordset".
717 if (call.Signature.ToString() != "s") {
718 reply = CreateUnknownMethodReply(call, "ExecChunkRecordset");
719 return;
722 if (call.Body == null) {
723 reply = VxDbus.CreateError(
724 "org.freedesktop.DBus.Error.InvalidSignature",
725 "Signature provided but no body received", call);
726 return;
728 /// XXX
730 VxDb.ExecChunkRecordset(conn, call, out reply);
732 // For debugging
733 VxDbus.MessageDump(" >> ", reply);
736 private static Signature VxColumnInfoToArraySignature(VxColumnInfo[] vxci)
738 StringBuilder sig = new StringBuilder("a(");
740 foreach (VxColumnInfo ci in vxci) {
741 switch (ci.VxColumnType) {
742 case VxColumnType.Int64:
743 sig.Append("x");
744 break;
745 case VxColumnType.Int32:
746 sig.Append("i");
747 break;
748 case VxColumnType.Int16:
749 sig.Append("n");
750 break;
751 case VxColumnType.UInt8:
752 sig.Append("y");
753 break;
754 case VxColumnType.Bool:
755 sig.Append("b");
756 break;
757 case VxColumnType.Double:
758 sig.Append("d");
759 break;
760 case VxColumnType.Uuid:
761 sig.Append("s");
762 break;
763 case VxColumnType.Binary:
764 sig.Append("ay");
765 break;
766 case VxColumnType.String:
767 sig.Append("s");
768 break;
769 case VxColumnType.DateTime:
770 sig.Append("(xi)");
771 break;
772 case VxColumnType.Decimal:
773 sig.Append("s");
774 break;
775 default:
776 throw new ArgumentException("Unknown VxColumnType");
780 sig.Append(")");
782 return new Signature(sig.ToString());
785 private static Type[] VxColumnInfoToType(VxColumnInfo[] vxci)
787 Type[] ret = new Type[vxci.Length];
789 for (int i=0; i < vxci.Length; i++) {
790 switch (vxci[i].VxColumnType) {
791 case VxColumnType.Int64:
792 ret[i] = typeof(Int64);
793 break;
794 case VxColumnType.Int32:
795 ret[i] = typeof(Int32);
796 break;
797 case VxColumnType.Int16:
798 ret[i] = typeof(Int16);
799 break;
800 case VxColumnType.UInt8:
801 ret[i] = typeof(Byte);
802 break;
803 case VxColumnType.Bool:
804 ret[i] = typeof(Boolean);
805 break;
806 case VxColumnType.Double:
807 ret[i] = typeof(Double);
808 break;
809 case VxColumnType.Uuid:
810 ret[i] = typeof(string);
811 break;
812 case VxColumnType.Binary:
813 ret[i] = typeof(byte[]);
814 break;
815 case VxColumnType.String:
816 ret[i] = typeof(string);
817 break;
818 case VxColumnType.DateTime:
819 ret[i] = typeof(VxDbusDateTime);
820 break;
821 case VxColumnType.Decimal:
822 ret[i] = typeof(string);
823 break;
824 default:
825 throw new ArgumentException("Unknown VxColumnType");
829 return ret;
832 private static void WriteStructArray(MessageWriter writer,
833 Type[] types, object[][] data)
835 foreach (object[] row in data) {
836 writer.WritePad(8);
838 for (int i=0; i < row.Length; i++) {
839 if (!types[i].IsInstanceOfType(row[i]))
840 throw new ArgumentException("Data does not match type for "
841 +"column " + i);
843 writer.Write(types[i], row[i]);
848 private static void CallGetSchemaChecksums(Connection conn,
849 Message call, out Message reply)
851 if (call.Signature.ToString() != "") {
852 reply = CreateUnknownMethodReply(call, "GetSchemaChecksums");
853 return;
856 string clientid = GetClientId(call);
857 if (clientid == null)
859 reply = VxDbus.CreateError(
860 "org.freedesktop.DBus.Error.Failed",
861 "Could not identify the client", call);
862 return;
865 // FIXME: Add vx.db.toomuchdata error
866 MessageWriter writer = new MessageWriter(Connection.NativeEndianness);
868 using (var dbi = VxSqlPool.create(clientid))
870 VxDbSchema backend = new VxDbSchema(dbi);
871 VxSchemaChecksums sums = backend.GetChecksums();
872 sums.WriteChecksums(writer);
875 reply = VxDbus.CreateReply(call,
876 VxSchemaChecksums.GetDbusSignature(), writer);
878 // For debugging
879 VxDbus.MessageDump(" >> ", reply);
882 private static void CallGetSchema(Connection conn,
883 Message call, out Message reply)
885 if (call.Signature.ToString() != "as") {
886 reply = CreateUnknownMethodReply(call, "GetSchema");
887 return;
890 string clientid = GetClientId(call);
891 if (clientid == null)
893 reply = VxDbus.CreateError(
894 "org.freedesktop.DBus.Error.Failed",
895 "Could not identify the client", call);
896 return;
899 var it = call.iter();
900 string[] names = it.pop().Cast<string>().ToArray();
902 MessageWriter writer = new MessageWriter(Connection.NativeEndianness);
904 using (var dbi = VxSqlPool.create(clientid))
906 VxDbSchema backend = new VxDbSchema(dbi);
907 VxSchema schema = backend.Get(names);
908 schema.WriteSchema(writer);
911 reply = VxDbus.CreateReply(call, VxSchema.GetDbusSignature(), writer);
913 // For debugging
914 VxDbus.MessageDump(" >> ", reply);
917 private static void CallDropSchema(Connection conn,
918 Message call, out Message reply)
920 if (call.Signature.ToString() != "as") {
921 reply = CreateUnknownMethodReply(call, "DropSchema");
922 return;
925 string clientid = GetClientId(call);
926 if (clientid == null)
928 reply = VxDbus.CreateError(
929 "org.freedesktop.DBus.Error.Failed",
930 "Could not identify the client", call);
931 return;
934 var it = call.iter();
935 string[] keys = it.pop().Cast<string>().ToArray();
937 VxSchemaErrors errs;
938 using (var dbi = VxSqlPool.create(clientid))
940 VxDbSchema backend = new VxDbSchema(dbi);
941 errs = backend.DropSchema(keys);
944 MessageWriter writer = new MessageWriter(Connection.NativeEndianness);
945 VxSchemaErrors.WriteErrors(writer, errs);
947 reply = VxDbus.CreateReply(call, VxSchemaErrors.GetDbusSignature(),
948 writer);
949 if (errs != null && errs.Count > 0)
951 reply.type = MessageType.Error;
952 reply.err = "org.freedesktop.DBus.Error.Failed";
956 private static void CallPutSchema(Connection conn,
957 Message call, out Message reply)
959 if (call.Signature.ToString() != String.Format("{0}i",
960 VxSchema.GetDbusSignature())) {
961 reply = CreateUnknownMethodReply(call, "PutSchema");
962 return;
965 string clientid = GetClientId(call);
966 if (clientid == null)
968 reply = VxDbus.CreateError(
969 "org.freedesktop.DBus.Error.Failed",
970 "Could not identify the client", call);
971 return;
974 var it = call.iter();
975 VxSchema schema = new VxSchema(it.pop());
976 int opts = it.pop();
978 VxSchemaErrors errs;
980 using (var dbi = VxSqlPool.create(clientid))
982 VxDbSchema backend = new VxDbSchema(dbi);
983 errs = backend.Put(schema, null, (VxPutOpts)opts);
986 MessageWriter writer = new MessageWriter(Connection.NativeEndianness);
987 VxSchemaErrors.WriteErrors(writer, errs);
989 reply = VxDbus.CreateReply(call, VxSchemaErrors.GetDbusSignature(),
990 writer);
991 if (errs != null && errs.Count > 0)
993 reply.type = MessageType.Error;
994 reply.err = "org.freedesktop.DBus.Error.Failed";
998 private static void CallGetSchemaData(Connection conn,
999 Message call, out Message reply)
1001 if (call.Signature.ToString() != "ss") {
1002 reply = CreateUnknownMethodReply(call, "GetSchemaData");
1003 return;
1006 string clientid = GetClientId(call);
1007 if (clientid == null)
1009 reply = VxDbus.CreateError(
1010 "org.freedesktop.DBus.Error.Failed",
1011 "Could not identify the client", call);
1012 return;
1015 var it = call.iter();
1016 string tablename = it.pop();
1017 string where = it.pop();
1019 MessageWriter writer = new MessageWriter(Connection.NativeEndianness);
1021 using (var dbi = VxSqlPool.create(clientid))
1023 VxDbSchema backend = new VxDbSchema(dbi);
1024 string schemadata = backend.GetSchemaData(tablename, 0, where);
1025 writer.Write(schemadata);
1028 reply = VxDbus.CreateReply(call, "s", writer);
1031 private static void CallPutSchemaData(Connection conn,
1032 Message call, out Message reply)
1034 if (call.Signature.ToString() != "ss") {
1035 reply = CreateUnknownMethodReply(call, "PutSchemaData");
1036 return;
1039 string clientid = GetClientId(call);
1040 if (clientid == null)
1042 reply = VxDbus.CreateError(
1043 "org.freedesktop.DBus.Error.Failed",
1044 "Could not identify the client", call);
1045 return;
1048 var it = call.iter();
1049 string tablename = it.pop();
1050 string text = it.pop();
1052 using (var dbi = VxSqlPool.create(clientid))
1054 VxDbSchema backend = new VxDbSchema(dbi);
1055 backend.PutSchemaData(tablename, text, 0);
1058 reply = VxDbus.CreateReply(call);
1061 public static MessageWriter PrepareRecordsetWriter(VxColumnInfo[] colinfo,
1062 object[][] data,
1063 byte[][] nulldata)
1065 MessageWriter writer = new MessageWriter(Connection.NativeEndianness);
1067 WriteColInfo(writer, colinfo);
1068 if (colinfo.Length <= 0)
1070 // Some clients can't parse a() (empty struct) properly, so
1071 // we'll have an empty array of (i) instead.
1072 writer.Write(typeof(Signature), new Signature("a(i)"));
1074 else
1075 writer.Write(typeof(Signature), VxColumnInfoToArraySignature(colinfo));
1076 writer.WriteDelegatePrependSize(delegate(MessageWriter w)
1078 WriteStructArray(w, VxColumnInfoToType(colinfo), data);
1079 }, 8);
1080 writer.Write(typeof(byte[][]), nulldata);
1082 return writer;