Transport no longer has a Stream member.
[versaplex.git] / versaplexd / vxapi.cs
blob9b9fa193a2fa4094db028fb9aee1605fb6c88689
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 VxColumnType coltype, out object result)
17 log.print(WvLog.L.Debug3, "ExecScalar {0}\n", query);
19 try
21 using (var dbi = VxSqlPool.create(connid))
22 using (var qr = dbi.select(query))
24 var ci = ProcessSchema(qr.columns);
25 if (ci.Length == 0)
27 coltype = VxColumnType.String;
28 result = "";
29 return;
32 coltype = ci[0].VxColumnType;
33 var en = qr.GetEnumerator();
34 if (en.MoveNext())
35 result = en.Current[0].inner;
36 else
38 coltype = VxColumnType.String;
39 result = "";
43 catch (DbException e)
45 throw new VxSqlException(e.Message, e);
50 internal static void SendChunkRecordSignal(Connection conn,
51 Message call, string sender,
52 VxColumnInfo[] colinfo,
53 object[][] data, byte[][] nulls)
55 MessageWriter writer =
56 VxDbInterfaceRouter.PrepareRecordsetWriter(colinfo, data, nulls);
57 writer.Write(call.serial);
59 Message signal = VxDbus.CreateSignal(sender, "ChunkRecordsetSig",
60 "a(issnny)vaayu",
61 writer);
63 // For debugging
64 VxDbus.MessageDump(" S>> ", signal);
66 conn.Send(signal);
70 internal static void ExecChunkRecordset(Connection conn,
71 Message call, out Message reply)
73 string connid = VxDbInterfaceRouter.GetClientId(call);
75 if (connid == null)
77 reply = VxDbus.CreateError(
78 "org.freedesktop.DBus.Error.Failed",
79 "Could not identify the client", call);
80 return;
83 var it = call.iter();
85 string query = it.pop();
86 string iquery = query.ToLower().Trim();
87 reply = null;
88 // XXX this is fishy, really... whitespace fucks it up.
90 if (iquery.StartsWith("list tables"))
91 query = "exec sp_tables";
92 else if (iquery.StartsWith("list columns "))
93 query = String.Format("exec sp_columns @table_name='{0}'",
94 query.Substring(13));
95 else if (iquery.StartsWith("list all table") &&
96 iquery.StartsWith("list all tablefunction") == false)
97 query = "select distinct cast(Name as varchar(max)) Name"
98 + " from sysobjects "
99 + " where objectproperty(id,'IsTable')=1 "
100 + " and xtype='U' "
101 + " order by Name ";
102 else if (iquery.StartsWith("list all"))
103 // Format: list all {view|trigger|procedure|scalarfunction|tablefunction}
104 // Returns: a list of all of whatever
105 query = String.Format(
106 "select distinct "
107 + " cast (object_name(id) as varchar(256)) Name "
108 + " from syscomments "
109 + " where objectproperty(id,'Is{0}') = 1 "
110 + " order by Name ",
111 query.Split(' ')[2].Trim());
112 else if (iquery.StartsWith("get object"))
113 // Format:
114 // get object {view|trigger|procedure|scalarfunction|tablefunction} name
115 // Returns: the "source code" to the object
116 query = String.Format(
117 "select cast(text as varchar(max)) text "
118 + "from syscomments "
119 + "where objectproperty(id, 'Is{0}') = 1 "
120 + "and object_name(id) = '{1}' "
121 + "order by number, colid ",
122 query.Split(' ')[2].Trim(),
123 query.Split(' ')[3].Trim());
124 else if (iquery.StartsWith("drop") || iquery.StartsWith("create") ||
125 iquery.StartsWith("insert") || iquery.StartsWith("update"))
127 //FIXME: Are the above the only ways to modify a DB, aka the only
128 // cases where we have to call the old ExecRecordSet?
129 //FIXME: This is an ugly way to handle these cases, but it works!
130 VxDbInterfaceRouter.CallExecRecordset(conn, call, out reply);
131 return;
134 log.print(WvLog.L.Debug3, "ExecChunkRecordset {0}\n", query);
136 string sender = call.sender;
140 // FIXME: Sadly, this is stupidly similar to ExecRecordset.
141 // Anything we can do here to identify commonalities?
142 using (var dbi = VxSqlPool.create(connid))
143 using (WvSqlRows resultset = dbi.select(query))
145 List<object[]> rows = new List<object[]>();
146 List<byte[]> rownulls = new List<byte[]>();
148 // Our size here is just an approximation. Start it at 1
149 // to ensure that at least one packet always gets sent.
150 int cursize = 1;
152 var columns = resultset.columns.ToArray();
153 VxColumnInfo[] colinfo = ProcessSchema(columns);
154 int ncols = columns.Count();
156 foreach (WvSqlRow cur_row in resultset)
158 object[] row = new object[ncols];
159 byte[] rownull = new byte[ncols];
160 cursize += rownull.Length;
162 for (int i = 0; i < ncols; i++)
164 WvAutoCast cval = cur_row[i];
165 bool isnull = cval.IsNull;
167 row[i] = null;
169 rownull[i] = isnull ? (byte)1 : (byte)0;
171 switch (colinfo[i].VxColumnType)
173 case VxColumnType.Int64:
174 row[i] = !isnull ?
175 (Int64)cval : new Int64();
176 cursize += sizeof(Int64);
177 break;
178 case VxColumnType.Int32:
179 row[i] = !isnull ?
180 (Int32)cval : new Int32();
181 cursize += sizeof(Int32);
182 break;
183 case VxColumnType.Int16:
184 row[i] = !isnull ?
185 (Int16)cval : new Int16();
186 cursize += sizeof(Int16);
187 break;
188 case VxColumnType.UInt8:
189 row[i] = !isnull ?
190 (Byte)cval : new Byte();
191 cursize += sizeof(Byte);
192 break;
193 case VxColumnType.Bool:
194 row[i] = !isnull ?
195 (bool)cval : new Boolean();
196 cursize += sizeof(Boolean);
197 break;
198 case VxColumnType.Double:
199 // Might return a Single or Double
200 // FIXME: Check if getting a single causes this
201 // to croak
202 row[i] = !isnull ?
203 (double)cval : (double)0.0;
204 cursize += sizeof(double);
205 break;
206 case VxColumnType.Uuid:
207 //FIXME: Do I work?
208 row[i] = !isnull ?
209 (string)cval : "";
210 cursize += !isnull ?
211 ((string)cval).Length * sizeof(Char) : 0;
212 break;
213 case VxColumnType.Binary:
215 if (isnull)
217 row[i] = new byte[0];
218 break;
221 row[i] = (byte[])cval;
222 cursize += ((byte[])cval).Length;
223 break;
225 case VxColumnType.String:
226 row[i] = !isnull ? (string)cval : "";
227 cursize += !isnull ?
228 ((string)cval).Length * sizeof(Char) : 0;
229 break;
230 case VxColumnType.DateTime:
231 row[i] = !isnull ?
232 new VxDbusDateTime((DateTime)cval) :
233 new VxDbusDateTime();
234 cursize += System.Runtime.InteropServices.Marshal.SizeOf((VxDbusDateTime)row[i]);
235 break;
236 case VxColumnType.Decimal:
237 row[i] = !isnull ?
238 ((Decimal)cval).ToString() : "";
239 cursize += ((string)(row[i])).Length *
240 sizeof(Char);
241 break;
243 } // column iterator
245 rows.Add(row);
246 rownulls.Add(rownull);
248 if (cursize >= 1024*1024) //approx 1 MB
250 log.print(WvLog.L.Debug4,
251 "(1 MB reached; {0} rows)\n",
252 rows.Count);
254 SendChunkRecordSignal(conn, call, sender, colinfo,
255 rows.ToArray(),
256 rownulls.ToArray());
258 rows = new List<object[]>();
259 rownulls = new List<byte[]>();
260 cursize = 0;
262 } // row iterator
264 if (cursize > 0)
266 log.print(WvLog.L.Debug4, "(Remaining data; {0} rows)\n",
267 rows.Count);
269 SendChunkRecordSignal(conn, call, sender, colinfo,
270 rows.ToArray(),
271 rownulls.ToArray());
273 } // using
274 MessageWriter replywriter = new MessageWriter();
275 replywriter.Write("ChunkRecordset sent you all your data!");
276 reply = VxDbus.CreateReply(call, "s", replywriter);
277 } catch (DbException e) {
278 throw new VxSqlException(e.Message, e);
282 internal static void ExecRecordset(string connid, string query,
283 out VxColumnInfo[] colinfo, out object[][] data,
284 out byte[][] nullity)
286 // XXX this is fishy
288 if (query.ToLower().StartsWith("list tables"))
289 query = "exec sp_tables";
290 else if (query.ToLower().StartsWith("list columns "))
291 query = String.Format("exec sp_columns @table_name='{0}'",
292 query.Substring(13));
293 else if (query.ToLower().StartsWith("list all table") &&
294 query.ToLower().StartsWith("list all tablefunction") == false)
295 query = "select distinct cast(Name as varchar(max)) Name"
296 + " from sysobjects "
297 + " where objectproperty(id,'IsTable')=1 "
298 + " and xtype='U' "
299 + " order by Name ";
300 else if (query.ToLower().StartsWith("list all"))
301 // Format: list all {view|trigger|procedure|scalarfunction|tablefunction}
302 // Returns: a list of all of whatever
303 query = String.Format(
304 "select distinct "
305 + " cast (object_name(id) as varchar(256)) Name "
306 + " from syscomments "
307 + " where objectproperty(id,'Is{0}') = 1 "
308 + " order by Name ",
309 query.Split(' ')[2].Trim());
310 else if (query.ToLower().StartsWith("get object"))
311 // Format:
312 // get object {view|trigger|procedure|scalarfunction|tablefunction} name
313 // Returns: the "source code" to the object
314 query = String.Format(
315 "select cast(text as varchar(max)) text "
316 + "from syscomments "
317 + "where objectproperty(id, 'Is{0}') = 1 "
318 + "and object_name(id) = '{1}' "
319 + "order by number, colid ",
320 query.Split(' ')[2].Trim(),
321 query.Split(' ')[3].Trim());
323 log.print(WvLog.L.Debug3, "ExecRecordset {0}\n", query);
325 try {
326 List<object[]> rows = new List<object[]>();
327 List<byte[]> rownulls = new List<byte[]>();
329 using (var dbi = VxSqlPool.create(connid))
330 using (var result = dbi.select(query))
332 var columns = result.columns.ToArray();
334 if (columns.Length <= 0)
335 log.print("No columns in resulting data set.");
337 colinfo = ProcessSchema(result.columns);
339 foreach (var r in result)
341 object[] row = new object[columns.Length];
342 byte[] rownull = new byte[columns.Length];
344 for (int i = 0; i < columns.Length; i++)
346 bool isnull = r[i].IsNull;
347 row[i] = null;
348 rownull[i] = isnull ? (byte)1 : (byte)0;
350 switch (colinfo[i].VxColumnType)
352 case VxColumnType.Int64:
353 row[i] = !isnull ? (Int64)r[i] : new Int64();
354 break;
355 case VxColumnType.Int32:
356 row[i] = !isnull ? (Int32)r[i] : new Int32();
357 break;
358 case VxColumnType.Int16:
359 row[i] = !isnull ? (Int16)r[i] : new Int16();
360 break;
361 case VxColumnType.UInt8:
362 row[i] = !isnull ? (Byte)r[i] : new Byte();
363 break;
364 case VxColumnType.Bool:
365 row[i] = !isnull ? (bool)r[i] : new Boolean();
366 break;
367 case VxColumnType.Double:
368 // Might return a Single or Double
369 // FIXME: Check if getting a single causes this
370 // to croak
371 row[i] = !isnull ? (double)r[i] : (double)0.0;
372 break;
373 case VxColumnType.Uuid:
374 row[i] = !isnull ? ((Guid)r[i]).ToString() : "";
375 break;
376 case VxColumnType.Binary:
377 row[i] = !isnull ? (byte[])r[i] : new byte[0];
378 break;
379 case VxColumnType.String:
380 row[i] = !isnull ? (string)r[i] : "";
381 break;
382 case VxColumnType.DateTime:
383 row[i] = !isnull ?
384 new VxDbusDateTime(r[i]) :
385 new VxDbusDateTime();
386 break;
387 case VxColumnType.Decimal:
388 row[i] = !isnull
389 ? ((decimal)r[i]).ToString() : "";
390 break;
394 rows.Add(row);
395 rownulls.Add(rownull);
398 data = rows.ToArray();
399 nullity = rownulls.ToArray();
400 log.print(WvLog.L.Debug4, "({0} rows)\n", data.Length);
401 wv.assert(nullity.Length == data.Length);
403 } catch (DbException e) {
404 throw new VxSqlException(e.Message, e);
408 private static VxColumnInfo[] ProcessSchema(IEnumerable<WvColInfo> columns)
410 // FIXME: This is stupidly similar to ProcessSchema
411 int ncols = columns.Count();
412 VxColumnInfo[] colinfo = new VxColumnInfo[ncols];
414 if (ncols <= 0)
415 return colinfo;
417 int i = 0;
419 foreach (WvColInfo col in columns)
421 System.Type type = col.type;
423 if (type == typeof(object))
425 // We're not even going to try to handle this yet
426 throw new VxBadSchemaException("Columns of type sql_variant "
427 + "are not supported by Versaplex at this time");
430 VxColumnType coltype;
432 if (type == typeof(Int64)) {
433 coltype = VxColumnType.Int64;
434 } else if (type == typeof(Int32)) {
435 coltype = VxColumnType.Int32;
436 } else if (type == typeof(Int16)) {
437 coltype = VxColumnType.Int16;
438 } else if (type == typeof(Byte)) {
439 coltype = VxColumnType.UInt8;
440 } else if (type == typeof(Boolean)) {
441 coltype = VxColumnType.Bool;
442 } else if (type == typeof(Single) || type == typeof(Double)) {
443 coltype = VxColumnType.Double;
444 } else if (type == typeof(Guid)) {
445 coltype = VxColumnType.Uuid;
446 } else if (type == typeof(Byte[])) {
447 coltype = VxColumnType.Binary;
448 } else if (type == typeof(string)) {
449 coltype = VxColumnType.String;
450 } else if (type == typeof(DateTime)) {
451 coltype = VxColumnType.DateTime;
452 } else if (type == typeof(Decimal)) {
453 coltype = VxColumnType.Decimal;
454 } else {
455 throw new VxBadSchemaException("Columns of type "
456 + type.ToString() + " are not supported by "
457 + "Versaplex at this time " +
458 "(column " + col.name + ")");
461 colinfo[i] = new VxColumnInfo(col.name, coltype,
462 col.nullable, col.size, col.precision, col.scale);
464 i++;
467 return colinfo;
471 public class VxDbInterfaceRouter : VxInterfaceRouter
474 static WvLog log = new WvLog("VxDbInterfaceRouter");
475 static readonly VxDbInterfaceRouter instance;
476 public static VxDbInterfaceRouter Instance {
477 get { return instance; }
480 static VxDbInterfaceRouter() {
481 instance = new VxDbInterfaceRouter();
484 private VxDbInterfaceRouter() : base("vx.db")
486 methods.Add("Test", CallTest);
487 methods.Add("Quit", CallQuit);
488 methods.Add("ExecScalar", CallExecScalar);
489 methods.Add("ExecRecordset", CallExecRecordset);
490 methods.Add("ExecChunkRecordset", CallExecChunkRecordset);
491 methods.Add("GetSchemaChecksums", CallGetSchemaChecksums);
492 methods.Add("GetSchema", CallGetSchema);
493 methods.Add("PutSchema", CallPutSchema);
494 methods.Add("DropSchema", CallDropSchema);
495 methods.Add("GetSchemaData", CallGetSchemaData);
496 methods.Add("PutSchemaData", CallPutSchemaData);
499 protected override void ExecuteCall(MethodCallProcessor processor,
500 Connection conn,
501 Message call, out Message reply)
503 try {
504 processor(conn, call, out reply);
505 } catch (VxRequestException e) {
506 reply = VxDbus.CreateError(e.DBusErrorType, e.Message, call);
507 log.print("SQL result: {0}\n", e.Short());
508 } catch (Exception e) {
509 reply = VxDbus.CreateError(
510 "vx.db.exception",
511 "An internal error occurred.", call);
512 log.print("{0}\n", e.ToString());
516 static Dictionary<string,string> usernames = new Dictionary<string, string>();
517 static Connection sessionbus = new Connection(Address.Session);
519 public static string GetClientId(Message call)
521 string sender = call.sender;
523 // For now, the client ID is just the username of the Unix UID that
524 // DBus has associated with the connection.
525 string username;
526 if (!usernames.TryGetValue(sender, out username))
530 // FIXME: We should be using VersaMain.conn here,
531 // not the session bus!!
532 // FIXME: This will likely change as we find a more
533 // universal way to do SSL authentication via D-Bus.
534 username = VxSqlPool.GetUsernameForCert(
535 sessionbus.GetCertFingerprint(sender));
537 catch
541 // FIXME: This system call isn't actually standard
542 // FIXME: we should be using VersaMain.conn here,
543 // not the session bus!!
544 username = sessionbus.GetUnixUserName(sender);
546 catch
550 // FIXME: This system call is standard, but not useful
551 // on Windows.
552 // FIXME: we should be using VersaMain.conn here,
553 // not the session bus!!
554 username = sessionbus.GetUnixUser(sender).ToString();
556 catch
558 username = "*"; // use default connection, if any
565 // Remember the result, so we don't have to ask DBus all the time
566 usernames[sender] = username;
568 log.print(WvLog.L.Info,
569 "New connection '{0}' is user '{1}'\n",
570 sender, username);
573 return username;
576 private static Message CreateUnknownMethodReply(Message call,
577 string methodname)
579 return VxDbus.CreateError(
580 "org.freedesktop.DBus.Error.UnknownMethod",
581 String.Format(
582 "No overload of {0} has signature '{1}'",
583 methodname, call.signature), call);
586 private static void CallTest(Connection conn,
587 Message call, out Message reply)
589 if (call.signature.ne()) {
590 reply = CreateUnknownMethodReply(call, "Test");
591 return;
594 string clientid = GetClientId(call);
595 if (clientid == null)
597 reply = VxDbus.CreateError(
598 "org.freedesktop.DBus.Error.Failed",
599 "Could not identify the client", call);
600 return;
603 VxColumnInfo[] colinfo;
604 object[][] data;
605 byte[][] nullity;
606 VxDb.ExecRecordset(clientid, "select 'Works! :D'",
607 out colinfo, out data, out nullity);
609 // FIXME: Add vx.db.toomuchdata error
610 MessageWriter writer = PrepareRecordsetWriter(colinfo, data, nullity);
612 reply = VxDbus.CreateReply(call, "a(issnny)vaay", writer);
614 // For debugging
615 VxDbus.MessageDump(" >> ", reply);
618 private static void CallQuit(Connection conn,
619 Message call, out Message reply)
621 // FIXME: Check permissions here
622 MessageWriter writer = new MessageWriter();
623 writer.Write("Quit");
624 reply = VxDbus.CreateReply(call, "s", writer);
625 VersaMain.want_to_die = true;
627 // For debugging
628 VxDbus.MessageDump(" >> ", reply);
631 private static void CallExecScalar(Connection conn,
632 Message call, out Message reply)
634 if (call.signature != "s") {
635 reply = CreateUnknownMethodReply(call, "ExecScalar");
636 return;
639 if (call.Body == null) {
640 reply = VxDbus.CreateError(
641 "org.freedesktop.DBus.Error.InvalidSignature",
642 "Signature provided but no body received", call);
643 return;
646 string clientid = GetClientId(call);
647 if (clientid == null)
649 reply = VxDbus.CreateError(
650 "org.freedesktop.DBus.Error.Failed",
651 "Could not identify the client", call);
652 return;
655 var it = call.iter();
656 string query = it.pop();
658 object result;
659 VxColumnType coltype;
660 VxDb.ExecScalar(clientid, (string)query,
661 out coltype, out result);
663 MessageWriter writer = new MessageWriter();
664 writer.WriteSig(VxColumnTypeToSignature(coltype));
665 WriteV(writer, coltype, result);
667 reply = VxDbus.CreateReply(call, "v", writer);
669 // For debugging
670 VxDbus.MessageDump(" >> ", reply);
673 static void WriteColInfo(MessageWriter writer, VxColumnInfo[] colinfo)
675 // a(issnny)
676 writer.WriteArray(8, colinfo, (w2, i) => {
677 w2.Write(i.size);
678 w2.Write(i.colname);
679 w2.Write(i.coltype.ToString());
680 w2.Write(i.precision);
681 w2.Write(i.scale);
682 w2.Write(i.nullable);
686 public static void CallExecRecordset(Connection conn,
687 Message call, out Message reply)
689 if (call.signature != "s") {
690 reply = CreateUnknownMethodReply(call, "ExecRecordset");
691 return;
694 if (call.Body == null) {
695 reply = VxDbus.CreateError(
696 "org.freedesktop.DBus.Error.InvalidSignature",
697 "Signature provided but no body received", call);
698 return;
701 string clientid = GetClientId(call);
702 if (clientid == null)
704 reply = VxDbus.CreateError(
705 "org.freedesktop.DBus.Error.Failed",
706 "Could not identify the client", call);
707 return;
710 var it = call.iter();
711 string query = it.pop();
713 VxColumnInfo[] colinfo;
714 object[][] data;
715 byte[][] nullity;
716 VxDb.ExecRecordset(clientid, (string)query,
717 out colinfo, out data, out nullity);
719 // FIXME: Add vx.db.toomuchdata error
720 MessageWriter writer = PrepareRecordsetWriter(colinfo, data, nullity);
722 reply = VxDbus.CreateReply(call, "a(issnny)vaay", writer);
724 // For debugging
725 VxDbus.MessageDump(" >> ", reply);
728 private static void CallExecChunkRecordset(Connection conn,
729 Message call, out Message reply)
731 // XXX: Stuff in this comment block shamelessly stolen from
732 // "CallExecRecordset".
733 if (call.signature != "s") {
734 reply = CreateUnknownMethodReply(call, "ExecChunkRecordset");
735 return;
738 if (call.Body == null) {
739 reply = VxDbus.CreateError(
740 "org.freedesktop.DBus.Error.InvalidSignature",
741 "Signature provided but no body received", call);
742 return;
744 /// XXX
746 VxDb.ExecChunkRecordset(conn, call, out reply);
748 // For debugging
749 VxDbus.MessageDump(" >> ", reply);
752 static string VxColumnTypeToSignature(VxColumnType t)
754 switch (t)
756 case VxColumnType.Int64:
757 return "x";
758 case VxColumnType.Int32:
759 return "i";
760 case VxColumnType.Int16:
761 return "n";
762 case VxColumnType.UInt8:
763 return "y";
764 case VxColumnType.Bool:
765 return "b";
766 case VxColumnType.Double:
767 return "d";
768 case VxColumnType.Uuid:
769 return "s";
770 case VxColumnType.Binary:
771 return "ay";
772 case VxColumnType.String:
773 return "s";
774 case VxColumnType.DateTime:
775 return "(xi)";
776 case VxColumnType.Decimal:
777 return "s";
778 default:
779 throw new ArgumentException("Unknown VxColumnType");
783 static string VxColumnInfoToArraySignature(VxColumnInfo[] vxci)
785 StringBuilder sig = new StringBuilder("a(");
786 foreach (VxColumnInfo ci in vxci)
787 sig.Append(VxColumnTypeToSignature(ci.VxColumnType));
788 sig.Append(")");
790 return sig.ToString();
793 private static void CallGetSchemaChecksums(Connection conn,
794 Message call, out Message reply)
796 if (call.signature.ne()) {
797 reply = CreateUnknownMethodReply(call, "GetSchemaChecksums");
798 return;
801 string clientid = GetClientId(call);
802 if (clientid == null)
804 reply = VxDbus.CreateError(
805 "org.freedesktop.DBus.Error.Failed",
806 "Could not identify the client", call);
807 return;
810 // FIXME: Add vx.db.toomuchdata error
811 MessageWriter writer = new MessageWriter();
813 using (var dbi = VxSqlPool.create(clientid))
815 VxDbSchema backend = new VxDbSchema(dbi);
816 VxSchemaChecksums sums = backend.GetChecksums();
817 sums.WriteChecksums(writer);
820 reply = VxDbus.CreateReply(call,
821 VxSchemaChecksums.GetDbusSignature(), writer);
823 // For debugging
824 VxDbus.MessageDump(" >> ", reply);
827 private static void CallGetSchema(Connection conn,
828 Message call, out Message reply)
830 if (call.signature != "as") {
831 reply = CreateUnknownMethodReply(call, "GetSchema");
832 return;
835 string clientid = GetClientId(call);
836 if (clientid == null)
838 reply = VxDbus.CreateError(
839 "org.freedesktop.DBus.Error.Failed",
840 "Could not identify the client", call);
841 return;
844 var it = call.iter();
845 string[] names = it.pop().Cast<string>().ToArray();
847 MessageWriter writer = new MessageWriter();
849 using (var dbi = VxSqlPool.create(clientid))
851 VxDbSchema backend = new VxDbSchema(dbi);
852 VxSchema schema = backend.Get(names);
853 schema.WriteSchema(writer);
856 reply = VxDbus.CreateReply(call, VxSchema.GetDbusSignature(), writer);
858 // For debugging
859 VxDbus.MessageDump(" >> ", reply);
862 private static void CallDropSchema(Connection conn,
863 Message call, out Message reply)
865 if (call.signature != "as") {
866 reply = CreateUnknownMethodReply(call, "DropSchema");
867 return;
870 string clientid = GetClientId(call);
871 if (clientid == null)
873 reply = VxDbus.CreateError(
874 "org.freedesktop.DBus.Error.Failed",
875 "Could not identify the client", call);
876 return;
879 var it = call.iter();
880 string[] keys = it.pop().Cast<string>().ToArray();
882 VxSchemaErrors errs;
883 using (var dbi = VxSqlPool.create(clientid))
885 VxDbSchema backend = new VxDbSchema(dbi);
886 errs = backend.DropSchema(keys);
889 MessageWriter writer = new MessageWriter();
890 VxSchemaErrors.WriteErrors(writer, errs);
892 reply = VxDbus.CreateReply(call, VxSchemaErrors.GetDbusSignature(),
893 writer);
894 if (errs != null && errs.Count > 0)
896 reply.type = MessageType.Error;
897 reply.err = "org.freedesktop.DBus.Error.Failed";
901 private static void CallPutSchema(Connection conn,
902 Message call, out Message reply)
904 if (call.signature != String.Format("{0}i",
905 VxSchema.GetDbusSignature())) {
906 reply = CreateUnknownMethodReply(call, "PutSchema");
907 return;
910 string clientid = GetClientId(call);
911 if (clientid == null)
913 reply = VxDbus.CreateError(
914 "org.freedesktop.DBus.Error.Failed",
915 "Could not identify the client", call);
916 return;
919 var it = call.iter();
920 VxSchema schema = new VxSchema(it.pop());
921 int opts = it.pop();
923 VxSchemaErrors errs;
925 using (var dbi = VxSqlPool.create(clientid))
927 VxDbSchema backend = new VxDbSchema(dbi);
928 errs = backend.Put(schema, null, (VxPutOpts)opts);
931 MessageWriter writer = new MessageWriter();
932 VxSchemaErrors.WriteErrors(writer, errs);
934 reply = VxDbus.CreateReply(call, VxSchemaErrors.GetDbusSignature(),
935 writer);
936 if (errs != null && errs.Count > 0)
938 reply.type = MessageType.Error;
939 reply.err = "org.freedesktop.DBus.Error.Failed";
943 private static void CallGetSchemaData(Connection conn,
944 Message call, out Message reply)
946 if (call.signature != "ss") {
947 reply = CreateUnknownMethodReply(call, "GetSchemaData");
948 return;
951 string clientid = GetClientId(call);
952 if (clientid == null)
954 reply = VxDbus.CreateError(
955 "org.freedesktop.DBus.Error.Failed",
956 "Could not identify the client", call);
957 return;
960 var it = call.iter();
961 string tablename = it.pop();
962 string where = it.pop();
964 MessageWriter writer = new MessageWriter();
966 using (var dbi = VxSqlPool.create(clientid))
968 VxDbSchema backend = new VxDbSchema(dbi);
969 string schemadata = backend.GetSchemaData(tablename, 0, where);
970 writer.Write(schemadata);
973 reply = VxDbus.CreateReply(call, "s", writer);
976 private static void CallPutSchemaData(Connection conn,
977 Message call, out Message reply)
979 if (call.signature != "ss") {
980 reply = CreateUnknownMethodReply(call, "PutSchemaData");
981 return;
984 string clientid = GetClientId(call);
985 if (clientid == null)
987 reply = VxDbus.CreateError(
988 "org.freedesktop.DBus.Error.Failed",
989 "Could not identify the client", call);
990 return;
993 var it = call.iter();
994 string tablename = it.pop();
995 string text = it.pop();
997 using (var dbi = VxSqlPool.create(clientid))
999 VxDbSchema backend = new VxDbSchema(dbi);
1000 backend.PutSchemaData(tablename, text, 0);
1003 reply = VxDbus.CreateReply(call);
1006 static void WriteV(MessageWriter w, VxColumnType t, object v)
1008 switch (t)
1010 case VxColumnType.Int64:
1011 w.Write((Int64)v);
1012 break;
1013 case VxColumnType.Int32:
1014 w.Write((Int32)v);
1015 break;
1016 case VxColumnType.Int16:
1017 w.Write((Int16)v);
1018 break;
1019 case VxColumnType.UInt8:
1020 w.Write((byte)v);
1021 break;
1022 case VxColumnType.Bool:
1023 w.Write((bool)v);
1024 break;
1025 case VxColumnType.Double:
1026 w.Write((double)v);
1027 break;
1028 case VxColumnType.Binary:
1029 w.Write((byte[])v);
1030 break;
1031 case VxColumnType.String:
1032 case VxColumnType.Decimal:
1033 case VxColumnType.Uuid:
1034 w.Write((string)v);
1035 break;
1036 case VxColumnType.DateTime:
1038 var dt = (VxDbusDateTime)v;
1039 w.Write(dt.seconds);
1040 w.Write(dt.microseconds);
1041 break;
1043 default:
1044 throw new ArgumentException("Unknown VxColumnType");
1048 // a(issnny)vaay
1049 public static MessageWriter PrepareRecordsetWriter(VxColumnInfo[] colinfo,
1050 object[][] data,
1051 byte[][] nulldata)
1053 MessageWriter writer = new MessageWriter();
1055 // a(issnny)
1056 WriteColInfo(writer, colinfo);
1058 // v
1059 if (colinfo.Length <= 0)
1061 // Some clients can't parse a() (empty struct) properly, so
1062 // we'll have an empty array of (i) instead.
1063 writer.WriteSig("a(i)");
1065 else
1066 writer.WriteSig(VxColumnInfoToArraySignature(colinfo));
1068 // a(whatever)
1069 writer.WriteArray(8, data, (w2, r) => {
1070 for (int i = 0; i < colinfo.Length; i++)
1071 WriteV(w2, colinfo[i].VxColumnType, r[i]);
1074 // aay
1075 writer.WriteArray(4, nulldata, (w2, r) => {
1076 w2.Write(r);
1079 return writer;