Oops, committed my log message cleanups a little too soon.
[versaplex.git] / versaplexd / vxapi.cs
blob8fc3489e2a87a4d7ce258a88fe4ce8431c9c36ee
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(WvDbus conn,
51 WvDbusMsg call, string sender,
52 VxColumnInfo[] colinfo,
53 object[][] data, byte[][] nulls)
55 WvDbusWriter writer =
56 VxDbusRouter.PrepareRecordsetWriter(colinfo, data, nulls);
57 writer.Write(call.serial);
59 new WvDbusSignal(call.sender, call.path, "vx.db", "ChunkRecordsetSig",
60 "a(issnny)vaayu")
61 .write(writer)
62 .send(conn);
66 internal static void ExecChunkRecordset(WvDbus conn,
67 WvDbusMsg call, out WvDbusMsg reply)
69 string connid = VxDbusRouter.GetClientId(call);
71 if (connid == null)
73 reply = call.err_reply(
74 "org.freedesktop.DBus.Error.Failed",
75 "Could not identify the client");
76 return;
79 var it = call.iter();
81 string query = it.pop();
82 string iquery = query.ToLower().Trim();
83 reply = null;
84 // XXX this is fishy, really... whitespace fucks it up.
86 if (iquery.StartsWith("list tables"))
87 query = "exec sp_tables";
88 else if (iquery.StartsWith("list columns "))
89 query = String.Format("exec sp_columns @table_name='{0}'",
90 query.Substring(13));
91 else if (iquery.StartsWith("list all table") &&
92 iquery.StartsWith("list all tablefunction") == false)
93 query = "select distinct cast(Name as varchar(max)) Name"
94 + " from sysobjects "
95 + " where objectproperty(id,'IsTable')=1 "
96 + " and xtype='U' "
97 + " order by Name ";
98 else if (iquery.StartsWith("list all"))
99 // Format: list all {view|trigger|procedure|scalarfunction|tablefunction}
100 // Returns: a list of all of whatever
101 query = String.Format(
102 "select distinct "
103 + " cast (object_name(id) as varchar(256)) Name "
104 + " from syscomments "
105 + " where objectproperty(id,'Is{0}') = 1 "
106 + " order by Name ",
107 query.Split(' ')[2].Trim());
108 else if (iquery.StartsWith("get object"))
109 // Format:
110 // get object {view|trigger|procedure|scalarfunction|tablefunction} name
111 // Returns: the "source code" to the object
112 query = String.Format(
113 "select cast(text as varchar(max)) text "
114 + "from syscomments "
115 + "where objectproperty(id, 'Is{0}') = 1 "
116 + "and object_name(id) = '{1}' "
117 + "order by number, colid ",
118 query.Split(' ')[2].Trim(),
119 query.Split(' ')[3].Trim());
121 log.print(WvLog.L.Debug3, "ExecChunkRecordset {0}\n", query);
123 string sender = call.sender;
127 // FIXME: Sadly, this is stupidly similar to ExecRecordset.
128 // Anything we can do here to identify commonalities?
129 using (var dbi = VxSqlPool.create(connid))
130 using (WvSqlRows resultset = dbi.select(query))
132 List<object[]> rows = new List<object[]>();
133 List<byte[]> rownulls = new List<byte[]>();
135 // Our size here is just an approximation.
136 int cursize = 0;
138 var columns = resultset.columns.ToArray();
139 VxColumnInfo[] colinfo = ProcessSchema(columns);
140 int ncols = columns.Count();
142 foreach (WvSqlRow cur_row in resultset)
144 object[] row = new object[ncols];
145 byte[] rownull = new byte[ncols];
146 cursize += rownull.Length;
148 for (int i = 0; i < ncols; i++)
150 WvAutoCast cval = cur_row[i];
151 bool isnull = cval.IsNull;
153 row[i] = null;
155 rownull[i] = isnull ? (byte)1 : (byte)0;
157 switch (colinfo[i].VxColumnType)
159 case VxColumnType.Int64:
160 row[i] = !isnull ?
161 (Int64)cval : new Int64();
162 cursize += sizeof(Int64);
163 break;
164 case VxColumnType.Int32:
165 row[i] = !isnull ?
166 (Int32)cval : new Int32();
167 cursize += sizeof(Int32);
168 break;
169 case VxColumnType.Int16:
170 row[i] = !isnull ?
171 (Int16)cval : new Int16();
172 cursize += sizeof(Int16);
173 break;
174 case VxColumnType.UInt8:
175 row[i] = !isnull ?
176 (Byte)cval : new Byte();
177 cursize += sizeof(Byte);
178 break;
179 case VxColumnType.Bool:
180 row[i] = !isnull ?
181 (bool)cval : new Boolean();
182 cursize += sizeof(Boolean);
183 break;
184 case VxColumnType.Double:
185 // Might return a Single or Double
186 // FIXME: Check if getting a single causes this
187 // to croak
188 row[i] = !isnull ?
189 (double)cval : (double)0.0;
190 cursize += sizeof(double);
191 break;
192 case VxColumnType.Uuid:
193 //FIXME: Do I work?
194 row[i] = !isnull ?
195 (string)cval : "";
196 cursize += !isnull ?
197 ((string)cval).Length * sizeof(Char) : 0;
198 break;
199 case VxColumnType.Binary:
201 if (isnull)
203 row[i] = new byte[0];
204 break;
207 row[i] = (byte[])cval;
208 cursize += ((byte[])cval).Length;
209 break;
211 case VxColumnType.String:
212 row[i] = !isnull ? (string)cval : "";
213 cursize += !isnull ?
214 ((string)cval).Length * sizeof(Char) : 0;
215 break;
216 case VxColumnType.DateTime:
217 row[i] = !isnull ?
218 new VxDbusDateTime((DateTime)cval) :
219 new VxDbusDateTime();
220 cursize += System.Runtime.InteropServices.Marshal.SizeOf((VxDbusDateTime)row[i]);
221 break;
222 case VxColumnType.Decimal:
223 row[i] = !isnull ?
224 ((Decimal)cval).ToString() : "";
225 cursize += ((string)(row[i])).Length *
226 sizeof(Char);
227 break;
229 } // column iterator
231 rows.Add(row);
232 rownulls.Add(rownull);
234 if (cursize >= 1024*1024) //approx 1 MB
236 log.print(WvLog.L.Debug4,
237 "(1 MB reached; {0} rows)\n",
238 rows.Count);
240 SendChunkRecordSignal(conn, call, sender, colinfo,
241 rows.ToArray(),
242 rownulls.ToArray());
244 rows = new List<object[]>();
245 rownulls = new List<byte[]>();
246 cursize = 0;
248 } // row iterator
250 // OK, we're down to either one more packet or no more data
251 // Package that up in the 'reply' to this message, saving some
252 // data being sent.
253 if (cursize > 0)
254 log.print(WvLog.L.Debug4, "(Remaining data; {0} rows)\n",
255 rows.Count);
257 // Create reply, either with or with no data
258 WvDbusWriter replywriter =
259 VxDbusRouter.PrepareRecordsetWriter(colinfo,
260 rows.ToArray(),
261 rownulls.ToArray());
262 reply = call.reply("a(issnny)vaay").write(replywriter);
263 } // using
264 } catch (DbException e) {
265 throw new VxSqlException(e.Message, e);
269 internal static void ExecRecordset(string connid, string query,
270 out VxColumnInfo[] colinfo, out object[][] data,
271 out byte[][] nullity)
273 // XXX this is fishy
275 if (query.ToLower().StartsWith("list tables"))
276 query = "exec sp_tables";
277 else if (query.ToLower().StartsWith("list columns "))
278 query = String.Format("exec sp_columns @table_name='{0}'",
279 query.Substring(13));
280 else if (query.ToLower().StartsWith("list all table") &&
281 query.ToLower().StartsWith("list all tablefunction") == false)
282 query = "select distinct cast(Name as varchar(max)) Name"
283 + " from sysobjects "
284 + " where objectproperty(id,'IsTable')=1 "
285 + " and xtype='U' "
286 + " order by Name ";
287 else if (query.ToLower().StartsWith("list all"))
288 // Format: list all {view|trigger|procedure|scalarfunction|tablefunction}
289 // Returns: a list of all of whatever
290 query = String.Format(
291 "select distinct "
292 + " cast (object_name(id) as varchar(256)) Name "
293 + " from syscomments "
294 + " where objectproperty(id,'Is{0}') = 1 "
295 + " order by Name ",
296 query.Split(' ')[2].Trim());
297 else if (query.ToLower().StartsWith("get object"))
298 // Format:
299 // get object {view|trigger|procedure|scalarfunction|tablefunction} name
300 // Returns: the "source code" to the object
301 query = String.Format(
302 "select cast(text as varchar(max)) text "
303 + "from syscomments "
304 + "where objectproperty(id, 'Is{0}') = 1 "
305 + "and object_name(id) = '{1}' "
306 + "order by number, colid ",
307 query.Split(' ')[2].Trim(),
308 query.Split(' ')[3].Trim());
310 log.print(WvLog.L.Debug3, "ExecRecordset {0}\n", query);
312 try {
313 List<object[]> rows = new List<object[]>();
314 List<byte[]> rownulls = new List<byte[]>();
316 using (var dbi = VxSqlPool.create(connid))
317 using (var result = dbi.select(query))
319 var columns = result.columns.ToArray();
321 if (columns.Length <= 0)
322 log.print("No columns in resulting data set.");
324 colinfo = ProcessSchema(result.columns);
326 foreach (var r in result)
328 object[] row = new object[columns.Length];
329 byte[] rownull = new byte[columns.Length];
331 for (int i = 0; i < columns.Length; i++)
333 bool isnull = r[i].IsNull;
334 row[i] = null;
335 rownull[i] = isnull ? (byte)1 : (byte)0;
337 switch (colinfo[i].VxColumnType)
339 case VxColumnType.Int64:
340 row[i] = !isnull ? (Int64)r[i] : new Int64();
341 break;
342 case VxColumnType.Int32:
343 row[i] = !isnull ? (Int32)r[i] : new Int32();
344 break;
345 case VxColumnType.Int16:
346 row[i] = !isnull ? (Int16)r[i] : new Int16();
347 break;
348 case VxColumnType.UInt8:
349 row[i] = !isnull ? (Byte)r[i] : new Byte();
350 break;
351 case VxColumnType.Bool:
352 row[i] = !isnull ? (bool)r[i] : new Boolean();
353 break;
354 case VxColumnType.Double:
355 // Might return a Single or Double
356 // FIXME: Check if getting a single causes this
357 // to croak
358 row[i] = !isnull ? (double)r[i] : (double)0.0;
359 break;
360 case VxColumnType.Uuid:
361 row[i] = !isnull ? ((Guid)r[i]).ToString() : "";
362 break;
363 case VxColumnType.Binary:
364 row[i] = !isnull ? (byte[])r[i] : new byte[0];
365 break;
366 case VxColumnType.String:
367 row[i] = !isnull ? (string)r[i] : "";
368 break;
369 case VxColumnType.DateTime:
370 row[i] = !isnull ?
371 new VxDbusDateTime(r[i]) :
372 new VxDbusDateTime();
373 break;
374 case VxColumnType.Decimal:
375 row[i] = !isnull
376 ? ((decimal)r[i]).ToString() : "";
377 break;
381 rows.Add(row);
382 rownulls.Add(rownull);
385 data = rows.ToArray();
386 nullity = rownulls.ToArray();
387 log.print(WvLog.L.Debug4, "({0} rows)\n", data.Length);
388 wv.assert(nullity.Length == data.Length);
390 } catch (DbException e) {
391 throw new VxSqlException(e.Message, e);
395 private static VxColumnInfo[] ProcessSchema(IEnumerable<WvColInfo> columns)
397 // FIXME: This is stupidly similar to ProcessSchema
398 int ncols = columns.Count();
399 VxColumnInfo[] colinfo = new VxColumnInfo[ncols];
401 if (ncols <= 0)
402 return colinfo;
404 int i = 0;
406 foreach (WvColInfo col in columns)
408 System.Type type = col.type;
410 if (type == typeof(object))
412 // We're not even going to try to handle this yet
413 throw new VxBadSchemaException("Columns of type sql_variant "
414 + "are not supported by Versaplex at this time");
417 VxColumnType coltype;
419 if (type == typeof(Int64)) {
420 coltype = VxColumnType.Int64;
421 } else if (type == typeof(Int32)) {
422 coltype = VxColumnType.Int32;
423 } else if (type == typeof(Int16)) {
424 coltype = VxColumnType.Int16;
425 } else if (type == typeof(Byte)) {
426 coltype = VxColumnType.UInt8;
427 } else if (type == typeof(Boolean)) {
428 coltype = VxColumnType.Bool;
429 } else if (type == typeof(Single) || type == typeof(Double)) {
430 coltype = VxColumnType.Double;
431 } else if (type == typeof(Guid)) {
432 coltype = VxColumnType.Uuid;
433 } else if (type == typeof(Byte[])) {
434 coltype = VxColumnType.Binary;
435 } else if (type == typeof(string)) {
436 coltype = VxColumnType.String;
437 } else if (type == typeof(DateTime)) {
438 coltype = VxColumnType.DateTime;
439 } else if (type == typeof(Decimal)) {
440 coltype = VxColumnType.Decimal;
441 } else {
442 throw new VxBadSchemaException("Columns of type "
443 + type.ToString() + " are not supported by "
444 + "Versaplex at this time " +
445 "(column " + col.name + ")");
448 colinfo[i] = new VxColumnInfo(col.name, coltype,
449 col.nullable, col.size, col.precision, col.scale);
451 i++;
454 return colinfo;
458 public class VxDbusRouter
460 static WvLog log = new WvLog("VxDbusRouter");
461 protected delegate
462 void MethodCallProcessor(WvDbus conn, WvDbusMsg call,
463 out WvDbusMsg reply);
465 public VxDbusRouter()
469 public bool route(WvDbus conn, WvDbusMsg msg, out WvDbusMsg reply)
471 MethodCallProcessor p;
473 if (msg.ifc != "vx.db" || msg.path != "/db")
475 reply = null;
476 return false;
479 if (msg.method == "Test")
480 p = CallTest;
481 else if (msg.method == "Quit")
482 p = CallQuit;
483 else if (msg.method == "ExecScalar")
484 p = CallExecScalar;
485 else if (msg.method == "ExecRecordset")
486 p = CallExecRecordset;
487 else if (msg.method == "ExecChunkRecordset")
488 p = CallExecChunkRecordset;
489 else if (msg.method == "GetSchemaChecksums")
490 p = CallGetSchemaChecksums;
491 else if (msg.method == "GetSchema")
492 p = CallGetSchema;
493 else if (msg.method == "PutSchema")
494 p = CallPutSchema;
495 else if (msg.method == "DropSchema")
496 p = CallDropSchema;
497 else if (msg.method == "GetSchemaData")
498 p = CallGetSchemaData;
499 else if (msg.method == "PutSchemaData")
500 p = CallPutSchemaData;
501 else
503 // FIXME: this should be done at a higher level somewhere
504 reply = msg.err_reply(
505 "org.freedesktop.DBus.Error.UnknownMethod",
506 "Method name {0} not found on interface {1}",
507 msg.method, msg.ifc);
508 return true;
510 ExecuteCall(p, conn, msg, out reply);
511 return true;
514 void ExecuteCall(MethodCallProcessor processor,
515 WvDbus conn,
516 WvDbusMsg call, out WvDbusMsg reply)
518 try {
519 processor(conn, call, out reply);
520 } catch (VxRequestException e) {
521 reply = call.err_reply(e.DBusErrorType, e.Message);
522 log.print("SQL result: {0}\n", e.Short());
523 } catch (Exception e) {
524 reply = call.err_reply("vx.db.exception",
525 "An internal error occurred.");
526 log.print("{0}\n", e.ToString());
530 static Dictionary<string,string> usernames = new Dictionary<string, string>();
532 public static string GetClientId(WvDbusMsg call)
534 string sender = call.sender;
536 // For now, the client ID is just the username of the Unix UID that
537 // DBus has associated with the connection.
538 string username;
539 var conn = VersaMain.conn;
540 if (!usernames.TryGetValue(sender, out username))
544 // FIXME: This will likely change as we find a more
545 // universal way to do SSL authentication via D-Bus.
546 username = VxSqlPool.GetUsernameForCert(
547 conn.GetCertFingerprint(sender));
549 catch
553 // FIXME: This system call isn't actually standard
554 username = conn.GetUnixUserName(sender);
556 catch
560 // FIXME: This system call is standard, but not useful
561 // on Windows.
562 username = conn.GetUnixUser(sender).ToString();
564 catch
566 username = "*"; // use default connection, if any
573 // Remember the result, so we don't have to ask DBus all the time
574 usernames[sender] = username;
576 log.print(WvLog.L.Info,
577 "New connection '{0}' is user '{1}'\n",
578 sender, username);
581 return username;
584 static WvDbusMsg CreateUnknownMethodReply(WvDbusMsg call,
585 string methodname)
587 return call.err_reply("org.freedesktop.DBus.Error.UnknownMethod",
588 "No overload of {0} has signature '{1}'",
589 methodname, call.signature);
592 static void CallTest(WvDbus conn,
593 WvDbusMsg call, out WvDbusMsg reply)
595 if (call.signature.ne()) {
596 reply = CreateUnknownMethodReply(call, "Test");
597 return;
600 string clientid = GetClientId(call);
601 if (clientid == null)
603 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
604 "Could not identify the client");
605 return;
608 VxColumnInfo[] colinfo;
609 object[][] data;
610 byte[][] nullity;
611 VxDb.ExecRecordset(clientid, "select 'Works! :D'",
612 out colinfo, out data, out nullity);
614 // FIXME: Add vx.db.toomuchdata error
615 WvDbusWriter writer = PrepareRecordsetWriter(colinfo, data, nullity);
616 reply = call.reply("a(issnny)vaay").write(writer);
619 static void CallQuit(WvDbus conn,
620 WvDbusMsg call, out WvDbusMsg reply)
622 // FIXME: Check permissions here
623 WvDbusWriter writer = new WvDbusWriter();
624 writer.Write("Quit");
625 reply = call.reply("s").write(writer);
626 VersaMain.want_to_die = true;
629 static void CallExecScalar(WvDbus conn,
630 WvDbusMsg call, out WvDbusMsg reply)
632 if (call.signature != "s") {
633 reply = CreateUnknownMethodReply(call, "ExecScalar");
634 return;
637 if (call.Body == null) {
638 reply = call.err_reply
639 ("org.freedesktop.DBus.Error.InvalidSignature",
640 "Signature provided but no body received");
641 return;
644 string clientid = GetClientId(call);
645 if (clientid == null)
647 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
648 "Could not identify the client");
649 return;
652 var it = call.iter();
653 string query = it.pop();
655 object result;
656 VxColumnType coltype;
657 VxDb.ExecScalar(clientid, (string)query,
658 out coltype, out result);
660 WvDbusWriter writer = new WvDbusWriter();
661 writer.WriteSig(VxColumnTypeToSignature(coltype));
662 WriteV(writer, coltype, result);
664 reply = call.reply("v").write(writer);
667 static void WriteColInfo(WvDbusWriter writer, VxColumnInfo[] colinfo)
669 // a(issnny)
670 writer.WriteArray(8, colinfo, (w2, i) => {
671 w2.Write(i.size);
672 w2.Write(i.colname);
673 w2.Write(i.coltype.ToString());
674 w2.Write(i.precision);
675 w2.Write(i.scale);
676 w2.Write(i.nullable);
680 public static void CallExecRecordset(WvDbus conn,
681 WvDbusMsg call, out WvDbusMsg reply)
683 if (call.signature != "s") {
684 reply = CreateUnknownMethodReply(call, "ExecRecordset");
685 return;
688 if (call.Body == null) {
689 reply = call.err_reply
690 ("org.freedesktop.DBus.Error.InvalidSignature",
691 "Signature provided but no body received");
692 return;
695 string clientid = GetClientId(call);
696 if (clientid == null)
698 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
699 "Could not identify the client");
700 return;
703 var it = call.iter();
704 string query = it.pop();
706 VxColumnInfo[] colinfo;
707 object[][] data;
708 byte[][] nullity;
709 VxDb.ExecRecordset(clientid, (string)query,
710 out colinfo, out data, out nullity);
712 // FIXME: Add vx.db.toomuchdata error
713 WvDbusWriter writer = PrepareRecordsetWriter(colinfo, data, nullity);
715 reply = call.reply("a(issnny)vaay").write(writer);
718 static void CallExecChunkRecordset(WvDbus conn,
719 WvDbusMsg call, out WvDbusMsg reply)
721 // XXX: Stuff in this comment block shamelessly stolen from
722 // "CallExecRecordset".
723 if (call.signature != "s") {
724 reply = CreateUnknownMethodReply(call, "ExecChunkRecordset");
725 return;
728 if (call.Body == null) {
729 reply = call.err_reply
730 ("org.freedesktop.DBus.Error.InvalidSignature",
731 "Signature provided but no body received");
732 return;
734 /// XXX
736 VxDb.ExecChunkRecordset(conn, call, out reply);
739 static string VxColumnTypeToSignature(VxColumnType t)
741 switch (t)
743 case VxColumnType.Int64:
744 return "x";
745 case VxColumnType.Int32:
746 return "i";
747 case VxColumnType.Int16:
748 return "n";
749 case VxColumnType.UInt8:
750 return "y";
751 case VxColumnType.Bool:
752 return "b";
753 case VxColumnType.Double:
754 return "d";
755 case VxColumnType.Uuid:
756 return "s";
757 case VxColumnType.Binary:
758 return "ay";
759 case VxColumnType.String:
760 return "s";
761 case VxColumnType.DateTime:
762 return "(xi)";
763 case VxColumnType.Decimal:
764 return "s";
765 default:
766 throw new ArgumentException("Unknown VxColumnType");
770 static string VxColumnInfoToArraySignature(VxColumnInfo[] vxci)
772 StringBuilder sig = new StringBuilder("a(");
773 foreach (VxColumnInfo ci in vxci)
774 sig.Append(VxColumnTypeToSignature(ci.VxColumnType));
775 sig.Append(")");
777 return sig.ToString();
780 static void CallGetSchemaChecksums(WvDbus conn,
781 WvDbusMsg call, out WvDbusMsg reply)
783 if (call.signature.ne()) {
784 reply = CreateUnknownMethodReply(call, "GetSchemaChecksums");
785 return;
788 string clientid = GetClientId(call);
789 if (clientid == null)
791 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
792 "Could not identify the client");
793 return;
796 // FIXME: Add vx.db.toomuchdata error
797 WvDbusWriter writer = new WvDbusWriter();
799 using (var dbi = VxSqlPool.create(clientid))
801 VxDbSchema backend = new VxDbSchema(dbi);
802 VxSchemaChecksums sums = backend.GetChecksums();
803 sums.WriteChecksums(writer);
806 reply = call.reply(VxSchemaChecksums.GetDbusSignature()).write(writer);
809 static void CallGetSchema(WvDbus conn,
810 WvDbusMsg call, out WvDbusMsg reply)
812 if (call.signature != "as") {
813 reply = CreateUnknownMethodReply(call, "GetSchema");
814 return;
817 string clientid = GetClientId(call);
818 if (clientid == null)
820 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
821 "Could not identify the client");
822 return;
825 var it = call.iter();
826 string[] names = it.pop().Cast<string>().ToArray();
828 WvDbusWriter writer = new WvDbusWriter();
830 using (var dbi = VxSqlPool.create(clientid))
832 VxDbSchema backend = new VxDbSchema(dbi);
833 VxSchema schema = backend.Get(names);
834 schema.WriteSchema(writer);
837 reply = call.reply(VxSchema.GetDbusSignature()).write(writer);
840 static void CallDropSchema(WvDbus conn,
841 WvDbusMsg call, out WvDbusMsg reply)
843 if (call.signature != "as") {
844 reply = CreateUnknownMethodReply(call, "DropSchema");
845 return;
848 string clientid = GetClientId(call);
849 if (clientid == null)
851 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
852 "Could not identify the client");
853 return;
856 var it = call.iter();
857 string[] keys = it.pop().Cast<string>().ToArray();
859 VxSchemaErrors errs;
860 using (var dbi = VxSqlPool.create(clientid))
862 VxDbSchema backend = new VxDbSchema(dbi);
863 errs = backend.DropSchema(keys);
866 WvDbusWriter writer = new WvDbusWriter();
867 VxSchemaErrors.WriteErrors(writer, errs);
869 reply = call.reply(VxSchemaErrors.GetDbusSignature()).write(writer);
870 if (errs != null && errs.Count > 0)
872 reply.type = Wv.Dbus.MType.Error;
873 reply.err = "org.freedesktop.DBus.Error.Failed";
877 static void CallPutSchema(WvDbus conn,
878 WvDbusMsg call, out WvDbusMsg reply)
880 if (call.signature != String.Format("{0}i",
881 VxSchema.GetDbusSignature())) {
882 reply = CreateUnknownMethodReply(call, "PutSchema");
883 return;
886 string clientid = GetClientId(call);
887 if (clientid == null)
889 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
890 "Could not identify the client");
891 return;
894 var it = call.iter();
895 VxSchema schema = new VxSchema(it.pop());
896 int opts = it.pop();
898 VxSchemaErrors errs;
900 using (var dbi = VxSqlPool.create(clientid))
902 VxDbSchema backend = new VxDbSchema(dbi);
903 errs = backend.Put(schema, null, (VxPutOpts)opts);
906 WvDbusWriter writer = new WvDbusWriter();
907 VxSchemaErrors.WriteErrors(writer, errs);
909 reply = call.reply(VxSchemaErrors.GetDbusSignature()).write(writer);
910 if (errs != null && errs.Count > 0)
912 reply.type = Wv.Dbus.MType.Error;
913 reply.err = "org.freedesktop.DBus.Error.Failed";
917 static void CallGetSchemaData(WvDbus conn,
918 WvDbusMsg call, out WvDbusMsg reply)
920 if (call.signature != "ss") {
921 reply = CreateUnknownMethodReply(call, "GetSchemaData");
922 return;
925 string clientid = GetClientId(call);
926 if (clientid == null)
928 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
929 "Could not identify the client");
930 return;
933 var it = call.iter();
934 string tablename = it.pop();
935 string where = it.pop();
937 WvDbusWriter writer = new WvDbusWriter();
939 using (var dbi = VxSqlPool.create(clientid))
941 VxDbSchema backend = new VxDbSchema(dbi);
942 string schemadata = backend.GetSchemaData(tablename, 0, where);
943 writer.Write(schemadata);
946 reply = call.reply("s").write(writer);
949 static void CallPutSchemaData(WvDbus conn,
950 WvDbusMsg call, out WvDbusMsg reply)
952 if (call.signature != "ss") {
953 reply = CreateUnknownMethodReply(call, "PutSchemaData");
954 return;
957 string clientid = GetClientId(call);
958 if (clientid == null)
960 reply = call.err_reply("org.freedesktop.DBus.Error.Failed",
961 "Could not identify the client");
962 return;
965 var it = call.iter();
966 string tablename = it.pop();
967 string text = it.pop();
969 using (var dbi = VxSqlPool.create(clientid))
971 VxDbSchema backend = new VxDbSchema(dbi);
972 backend.PutSchemaData(tablename, text, 0);
975 reply = call.reply();
978 static void WriteV(WvDbusWriter w, VxColumnType t, object v)
980 switch (t)
982 case VxColumnType.Int64:
983 w.Write((Int64)v);
984 break;
985 case VxColumnType.Int32:
986 w.Write((Int32)v);
987 break;
988 case VxColumnType.Int16:
989 w.Write((Int16)v);
990 break;
991 case VxColumnType.UInt8:
992 w.Write((byte)v);
993 break;
994 case VxColumnType.Bool:
995 w.Write((bool)v);
996 break;
997 case VxColumnType.Double:
998 w.Write((double)v);
999 break;
1000 case VxColumnType.Binary:
1001 w.Write((byte[])v);
1002 break;
1003 case VxColumnType.String:
1004 case VxColumnType.Decimal:
1005 case VxColumnType.Uuid:
1006 w.Write((string)v);
1007 break;
1008 case VxColumnType.DateTime:
1010 var dt = (VxDbusDateTime)v;
1011 w.Write(dt.seconds);
1012 w.Write(dt.microseconds);
1013 break;
1015 default:
1016 throw new ArgumentException("Unknown VxColumnType");
1020 // a(issnny)vaay
1021 public static WvDbusWriter PrepareRecordsetWriter(VxColumnInfo[] colinfo,
1022 object[][] data,
1023 byte[][] nulldata)
1025 WvDbusWriter writer = new WvDbusWriter();
1027 // a(issnny)
1028 WriteColInfo(writer, colinfo);
1030 // v
1031 if (colinfo.Length <= 0)
1033 // Some clients can't parse a() (empty struct) properly, so
1034 // we'll have an empty array of (i) instead.
1035 writer.WriteSig("a(i)");
1037 else
1038 writer.WriteSig(VxColumnInfoToArraySignature(colinfo));
1040 // a(whatever)
1041 writer.WriteArray(8, data, (w2, r) => {
1042 for (int i = 0; i < colinfo.Length; i++)
1043 WriteV(w2, colinfo[i].VxColumnType, r[i]);
1046 // aay
1047 writer.WriteArray(4, nulldata, (w2, r) => {
1048 w2.Write(r);
1051 return writer;