layer:11

an addiction to or an obsession with acquiring, manipulating, and sharing information

Connecting to Cassandra with C# and Thrift

Here’s something I wanted to write down for a long time now, because every time I have to do this (when Cassandra changes its interface), I have already forgotten what and exactly how needs to be done. Let’s assume that we have Cassandra up and running, because most distributions should have a package ready to use, or at least a build script. And now we want to connect to it with C#. What we need is two C# libraries, Thrift and Cassandra. So, let’s build them from source.

What exactly is Thrift? It’s a framework for building services and clients for those services - you write a file that describes what you want, then use the Thrift compiler to get a server skeleton and a RPC client in any language that Thrift supports.

First thing we need is a Thrift compiler and a Thrift library for C#. Let’s download the Thrift source and simply ./configure && make it, no need to install. You might want to disable support for other languages, they don’t get in the way, but I had problems building a Ruby library from Thrift 0.7.0. Now we have a C# Thrift library (lib/csharp/Thrift.dll) and a Thrift compiler (compiler/cpp/thrift). This library doesn’t give us much by itself, but it’s required for the Cassandra library, that we’ll generate with the Thrift compiler.

Let’s get the Thrift definition file, that describes how Cassandra works, it’s interface/cassandra.thrift from the Cassandra source. Now we can use the Thrift compiler, to generate C# source files for Cassandra client: ./thrift -gen csharp cassandra.thrift. Output is in gen-csharp/Apache/Cassandra, just cd there and build it all: dmcs -r:Thrift -t:library -out:Apache.Cassandra.dll *.cs. Don’t forget to copy Thrift.dll there, or add the appropriate -lib:.

That should be it, we have all we need - a Thrift library (Thrift.dll) and a Cassandra client library (Apache.Cassandra.dll).

Web Server In 50 Lines

Web servers are useful, but not always you want or can install and configure one. Here’s a self contained, functional, multi-threaded and extensible web server in just few lines of C#.

 1 using System;
 2 using System.IO;
 3 using System.Net;
 4 using System.Text;
 5 
 6 
 7 class Server {
 8 
 9   static String root = "/home/you/web/root/";
10 
11   static String prefix = "http://localhost:8000/";
12 
13   static void Main(String[] args) {
14     using (var http = new HttpListener()) {
15       http.Prefixes.Add(prefix);
16       http.Start();
17       while (http.IsListening) {
18         var callback = new AsyncCallback(dispatch);
19         var result = http.BeginGetContext(callback, http);
20         result.AsyncWaitHandle.WaitOne();
21       }
22     }
23   }
24 
25   static void dispatch(IAsyncResult result) {
26     var http = (HttpListener)result.AsyncState;
27     var context = http.EndGetContext(result);
28     using (var responseStream = new MemoryStream()) {
29       var localPath = Path.Combine(root, context.Request.Url.LocalPath.Substring(1));
30       if (File.Exists(localPath)) {
31         using (var file = File.OpenRead(localPath)) {
32           file.CopyTo(responseStream);
33         }
34       }
35       else {
36         var text = Encoding.UTF8.GetBytes("404");
37         responseStream.Write(text, 0, text.Length);
38       }
39       context.Response.ContentLength64 = responseStream.Length;
40       responseStream.WriteTo(context.Response.OutputStream);
41       context.Response.OutputStream.Close();
42     }
43   }
44 }

So, you start up a HttpListener and pass every request it receives to dispatch(), there you can get a context that holds a HttpRequest and a HttpResponse, that you know and love, from there on, you’re free on how exactly you create the response, I check, if the requested URL can be mapped to a file, if so, I return that file, if not, I simply write “404”. That should give you a good idea, on how to expand this to match your needs.

Works quite well in my experience. One thing you have to be careful about, is exceptions in dispatch(), as they are in a new thread, you obviously won’t see them in Main().

2011-09-15 22:07

C Sharp.NETMono

Transpose Rows To Columns

One very common problem when making reports from a RDBMS, is that you need to show, data that’s stored in rows, in columns. For example, from data like this:

 1 CREATE TABLE #data_a (
 2   cat varchar(20) NOT NULL,
 3   mon date NOT NULL,
 4   val int NOT NULL
 5 );
 6 INSERT INTO #data_a
 7 VALUES
 8   ('Foo', '2011-02-01', '752'),
 9   ('Qux', '2011-03-01', '700'),
10   ('Qux', '2011-03-01', '936'),
11   ('Bar', '2011-02-01', '899'),
12   ('Foo', '2011-01-01', '482'),
13   ('Qux', '2011-01-01', '349'),
14   ('Foo', '2011-03-01', '67'),
15   ('Bar', '2011-02-01', '999'),
16   ('Bar', '2011-01-01', '183');

You would need to show, for example, the first three months, of the current year, in a table with a column for each month. A straightforward way to achieve this would be:

 1 SELECT
 2   d.cat,
 3   SUM(CASE WHEN MONTH(d.mon) = 1 THEN d.val ELSE 0 END) AS january_sum,
 4   SUM(CASE WHEN MONTH(d.mon) = 2 THEN d.val ELSE 0 END) AS february_sum,
 5   SUM(CASE WHEN MONTH(d.mon) = 3 THEN d.val ELSE 0 END) AS march_sum
 6 FROM #data_a AS d
 7 WHERE
 8   YEAR(d.mon) = YEAR(CURRENT_TIMESTAMP) AND
 9   MONTH(d.mon) IN (1, 2, 3)
10 GROUP BY d.cat
11 ORDER BY d.cat;

Works fine, easy to use and understand. A bit more advanced solution would be with PIVOT():

 1 SELECT
 2   p.cat,
 3   COALESCE(p.January, 0) AS january_sum,
 4   COALESCE(p.February, 0) AS february_sum,
 5   COALESCE(p.March, 0) AS march_sum
 6 FROM (
 7   SELECT
 8     d.cat,
 9     DATENAME(MONTH, d.mon) AS col,
10     d.val
11   FROM #data_a AS d
12   WHERE
13     YEAR(d.mon) = YEAR(CURRENT_TIMESTAMP) AND
14     MONTH(d.mon) IN (1, 2, 3)
15 ) AS d
16 PIVOT(
17   SUM(d.val)
18   FOR d.col IN (
19     January,
20     February,
21     March
22   )
23 ) AS p
24 ORDER BY p.cat;

But I don’t feel like there’s a good reason to use it, no benefit and more awkward syntax. I’ll try to give a short explanation on what goes into PIVOT(). SUM(d.val) says to aggregate d.val by summing it, FOR d.col means that you want to move values from d.col to new columns, IN (January, February, March) determines which values get moved to new columns (and the name of those columns). Another thing to note, p.cat in SELECT, not d.cat.

OK, how about a bit different situation:

 1 CREATE TABLE #data_b (
 2   cat varchar(20) NOT NULL,
 3   mon date NOT NULL,
 4   val char(5) NOT NULL,
 5   UNIQUE (cat, mon)
 6 );
 7 INSERT INTO #data_b
 8 VALUES
 9   ('Foo', '2011-01-01', 'CDHDE'),
10   ('Qux', '2011-01-01', 'DTDLI'),
11   ('Qux', '2011-03-01', 'PIQXG'),
12   ('Bar', '2011-02-01', 'TVDDI'),
13   ('Foo', '2011-02-01', 'VJKRC'),
14   ('Bar', '2011-03-01', 'XWMAG'),
15   ('Qux', '2011-02-01', 'GCIPP');

As we can’t aggregate those values, we end up with something like this:

 1 SELECT
 2   d.cat,
 3   j.val AS january_val,
 4   f.val AS february_val,
 5   m.val AS march_val
 6 FROM (
 7   SELECT DISTINCT d.cat
 8   FROM #data_b AS d
 9 ) AS d
10 LEFT JOIN #data_b AS j ON
11   j.cat = d.cat AND
12   YEAR(j.mon) = YEAR(CURRENT_TIMESTAMP) AND
13   MONTH(j.mon) = 1
14 LEFT JOIN #data_b AS f ON
15   f.cat = d.cat AND
16   YEAR(f.mon) = YEAR(CURRENT_TIMESTAMP) AND
17   MONTH(f.mon) = 2
18 LEFT JOIN #data_b AS m ON
19   m.cat = d.cat AND
20   YEAR(m.mon) = YEAR(CURRENT_TIMESTAMP) AND
21   MONTH(m.mon) = 3
22 ORDER BY d.cat;

Not pretty, but works fine and is very simple.

Now to what I actually wanted to write down. While these three solutions work perfectly for some cases, they aren’t useful for others. What if you didn’t know the columns beforehand? Say, maybe you need to have a column for every day in a month? In one month you would have 31 column, in other 28 columns. Also, you have to have relative column names. In previous examples, you can’t name a column like “2011-01-01”, because that would work only for this year. That’s not a big problem with dates, but what if you’re moving user entered text into columns? It wouldn’t be possible to make sense of the result. So, here’s a decent solution for all that:

 1 DECLARE @s varchar(MAX);
 2 DECLARE @j varchar(MAX);
 3 
 4 SELECT
 5   @s = COALESCE(@s + ', ', '') + '[' + t.col + '].val AS [' + t.col + ']',
 6   @j = COALESCE(@j + ' ', '') + 'LEFT JOIN #data_b AS [' + t.col + '] ON [' + t.col + '].cat = d.cat AND [' + t.col + '].mon = ''' + CAST(t.mon AS varchar) + ''''
 7 FROM (
 8   SELECT DISTINCT
 9     d.mon,
10     LOWER(DATENAME(MONTH, d.mon)) + '_val' AS col
11     FROM #data_b AS d
12     WHERE
13       YEAR(d.mon) = YEAR(CURRENT_TIMESTAMP) AND
14       MONTH(d.mon) IN (1, 2, 3)
15 ) AS t
16 ORDER BY t.mon;
17 
18 EXEC('
19   SELECT
20     d.cat,
21     ' + @s + '
22   FROM (
23     SELECT DISTINCT d.cat
24     FROM #data_b AS d
25   ) AS d
26   ' + @j + '
27   ORDER BY d.cat;
28 ');

I wouldn’t go as far as calling a dynamic SQL solution elegant, but I do think it’s simple and easy to fallow. Basically, you just dynamically create a query similar to the third solution. The cute trick here is how COALESCE() and SELECT @foo = works, it concatenates each row into one string. No need to use ugly WHILE loops, that I see everywhere. That’s it. BTW, as EXEC() executes in it’s own context, to get data out of it, you may want to use a global temporary table.

2011-09-07 21:52

SQLSQL Server

Error Logging

One of the very first things I do, for a new ASP.NET application, is to setup a decent error logging. I want to know when and why my application crashes and I don’t want to relay on users accurately reporting every error. It’s quite easy to do this using HttpApplication.Error event:

  1 using System;
  2 using System.Configuration;
  3 using System.Data;
  4 using System.Data.SqlClient;
  5 using System.Security.Cryptography;
  6 using System.Text;
  7 using System.Web;
  8 
  9 
 10 public class Application : HttpApplication {
 11 
 12   private String Md5Sum(String message) {
 13     var bytes = Encoding.UTF8.GetBytes(message);
 14     var digest = (new MD5CryptoServiceProvider()).ComputeHash(bytes);
 15     var checksum = new StringBuilder();
 16     foreach (var b in digest) {
 17       checksum.Append(b.ToString("X2"));
 18     }
 19     return checksum.ToString();
 20   }
 21 
 22   private void LogError(String checksum, Int32 httpCode, String type, String message, String stack, String source, String url, String user, String machine, String os, String version) {
 23     var connectionString = ConfigurationManager.ConnectionStrings["DB"].ConnectionString;
 24     using (var connection = new SqlConnection(connectionString)) {
 25       connection.Open();
 26       var command = connection.CreateCommand();
 27       command.CommandText = @"
 28         UPDATE errors
 29         SET
 30           last_occurrence = current_timestamp,
 31           occurrences = occurrences + 1,
 32           http_code = @http_code,
 33           type = @type,
 34           message = @message,
 35           source = @source,
 36           url = @url,
 37           [user] = @user,
 38           machine = @machine,
 39           os = @os,
 40           version = @version
 41         WHERE
 42           checksum = @checksum;
 43       ";
 44       command.Parameters.Add("http_code", SqlDbType.SmallInt).Value = httpCode;
 45       command.Parameters.Add("type", SqlDbType.VarChar, 250).Value = type;
 46       command.Parameters.Add("message", SqlDbType.VarChar, 1000).Value = message;
 47       command.Parameters.Add("source", SqlDbType.VarChar, 100).Value = source;
 48       command.Parameters.Add("url", SqlDbType.VarChar, 150).Value = url;
 49       command.Parameters.Add("user", SqlDbType.VarChar, 50).Value = user;
 50       command.Parameters.Add("machine", SqlDbType.VarChar, 50).Value = machine;
 51       command.Parameters.Add("os", SqlDbType.VarChar, 100).Value = os;
 52       command.Parameters.Add("version", SqlDbType.VarChar, 100).Value = version;
 53       command.Parameters.Add("checksum", SqlDbType.VarChar, 32).Value = checksum;
 54       if (command.ExecuteNonQuery() == 0) {
 55         command.CommandText = @"
 56           INSERT INTO errors (checksum, http_code, type, message, stack, source, url, [user], machine, os, version)
 57           VALUES (@checksum, @http_code, @type, @message, @stack, @source, @url, @user, @machine, @os, @version);
 58         ";
 59         command.Parameters.Add("stack", SqlDbType.Text).Value = stack;
 60         command.ExecuteNonQuery();
 61       }
 62     }
 63   }
 64 
 65   public void Application_Error(Object sender, EventArgs e) {
 66     Server.ClearError();
 67     Response.Clear();
 68     var exception = Server.GetLastError().GetBaseException();
 69     var stack = exception.StackTrace;
 70     var checksum = Md5Sum(stack);
 71     var httpCode = ((HttpException)Server.GetLastError()).GetHttpCode();
 72     var httpMessage = Response.StatusDescription;
 73     var type = exception.GetType().ToString();
 74     var message = exception.Message;
 75     var source = exception.Source;
 76     var url = Request.Path;
 77     var user = Environment.UserName;
 78     //var user = Request.ServerVariables["AUTH_USER"];
 79     var machine = Environment.MachineName;
 80     var os = Environment.OSVersion.ToString();
 81     var version = Environment.Version.ToString();
 82     var dateTime = DateTime.UtcNow.ToString("o");
 83     var details = "";
 84     var developers = ConfigurationManager.AppSettings["Developers"].Split(';');
 85     //if (Array.IndexOf(developers, Request.ServerVariables["REMOTE_ADDR"]) > -1) {
 86     if (Array.IndexOf(developers, user) > -1) {
 87       details = String.Format(@"
 88         <p>[<b>{0}</b>: {1}]</p>
 89         <pre><code>{2}</code></pre>
 90         <hr/>
 91         <p>{3} ({4}), {5}<i> -- {6} @ {7} ({8}/{9})</i></p>
 92       ", type, message, stack, source, url, dateTime, user, machine, os, version);
 93     }
 94     var page = String.Format(@"
 95       <?xml version=""1.0"" encoding=""UTF-8""?>
 96       <!DOCTYPE html PUBLIC ""-//W3C//DTD XHTML 1.1//EN"" ""http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"">
 97       <html xmlns=""http://www.w3.org/1999/xhtml"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:schemaLocation=""http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd"" xml:lang=""en"">
 98         <head>
 99           <title>{0} - {1}</title>
100         </head>
101         <body style=""font-size:smaller;"">
102           <p style=""text-align:center;color:white;background:orangered;""><b>{2}</b></p>
103           {3}
104         </body>
105       </html>
106     ", httpCode, httpMessage, checksum, details);
107     Response.Write(page);
108     Response.StatusCode = httpCode;
109     LogError(checksum, httpCode, type, message, stack, source, url, user, machine, os, version);
110   }
111 
112 }

You could put these methods directly into global.asax, but I prefer to compile and drop the DLL into /bin, or to save everything as a file with .cs extension into /app_code. Two later cases requires this one line in global.asax:

1 <%@ Inherits="Application" %>

It’s probably not a good idea to display error details to every user, just a checksum should be enough, so we’ll put users that need to see full errors into web.config:

1 <?xml version="1.0" encoding="utf-8"?>
2 <configuration>
3   <appSettings>
4     <add key="Developers" value="Alice;Bob" />
5   </appSettings>
6 </configuration>

One tricky thing about all this, is exceptions thrown from Application_Error() method. It would make sense, if these exceptions would show up as regular ASP.NET errors, but that’s not how it works in my experience. To show our own error page, we need to clear the error that resulted in Application_Error() being called, if we don’t call ClearError(), the original error will get displayed instead of our own, this also means that, if an exception gets thrown before ClearError(), you won’t see it. Situation with exceptions after ClearError() is a bit mysterious, on .NET I get a blank page, on Mono I get a regular ASP.NET error. Just know that, if something isn’t working as expected, first thing to try is to wrap everything in try-catch, to see if there’s any hidden exceptions.

Here’s the table definition, if you decide to use something similar:

 1 CREATE TABLE errors (
 2   id smallint IDENTITY NOT NULL,
 3   checksum char(32) NOT NULL UNIQUE,
 4   last_occurrence smalldatetime NOT NULL DEFAULT current_timestamp,
 5   occurrences int NOT NULL DEFAULT 1,
 6   http_code smallint NOT NULL,
 7   type varchar(250) NOT NULL,
 8   message varchar(1000) NOT NULL,
 9   stack text NOT NULL,
10   source varchar(100) NOT NULL,
11   url varchar(150) NOT NULL,
12   [user] varchar(50) NOT NULL,
13   machine varchar(50) NOT NULL,
14   os varchar(100) NOT NULL,
15   version varchar(100) NOT NULL,
16   CONSTRAINT PK_errors PRIMARY KEY CLUSTERED (id)
17 );

Undo Tree

A while ago, when I played around with E Text Editor, I was impressed by what they call a “Personal Revision Control”. It’s a nice concept, where instead of linear list of changes, which you can undo and redo, you can have branches.

For example, if you type “one”, then change it to “two”, then undo back to “one” and change it to “three”. In a typical text editor, now you would be able to go back to “one”, but not “two”. With E, you could switch between all changes, because it creates a new branch, when you go back and add a change.

Recently I discovered that Vim has had a feature like this (sans the pretty UI) for a long time, it’s called “undo tree”. Check out the documentation, but basically you can view branches with :undolist and time travel with :earlier and :later.

2010-02-23 22:25

Vim

Objects

Trying to understand how objects work in JavaScript can be confusing, not because it’s complicated or difficult, but because the language is flexible enough to allow different approaches and also because many programmers simply aren’t familiar with prototype-based programming. I’ll try to demonstrate, what I think is the simplest and cleanest way to work with objects in JavaScript. You can simply copy, paste and run in Firebug all of the following code.

So, lets define our own object, with one property and a method:

 1 var Box = function() {
 2 
 3   this.element = null;
 4 
 5   this.create = function() {
 6     if (!this.element) {
 7       this.element = document.createElement('div');
 8       with (this.element.style) {
 9         position = 'absolute';
10         width = '100px';
11         height = '100px';
12         top = '0';
13         left = '0';
14         backgroundColor = 'deepskyblue';
15         border = '1px solid dodgerblue';
16       }
17       document.body.appendChild(this.element);
18     }
19   };
20 
21 };

Now you can instantiate this Box with:

1 var myBox = new Box();
2 myBox.create();

What’s nice is that you can extend it at any time, for example, if we wanted to, not only create, but also remove boxes:

1 Box.prototype.destroy = function() {
2   if (this.element) {
3     this.element.parentNode.removeChild(this.element);
4     this.element = null;
5   }
6 }
7 myBox.destroy();

And inheritance isn’t difficult either - here’s how to create Circle that inherits properties and methods from Box and also we’ll override create method:

 1 var Circle = function() {
 2 
 3   this.create = function() {
 4     if (!this.element) {
 5       this.element = document.createElement('div');
 6       with (this.element.style) {
 7         position = 'absolute';
 8         width = '100px';
 9         height = '100px';
10         top = '0';
11         left = '0';
12         MozBorderRadius = '50px';
13         WebkitBorderRadius = '50px';
14         backgroundColor = 'hotpink';
15         border = '1px solid red';
16       }
17       document.body.appendChild(this.element);
18     }
19   };
20 
21 };
22 Circle.prototype = new Box();

And that’s really it, we’re done. Now something fun like this is possible:

 1 Box.prototype.moveTo = function(aTop, aLeft) {
 2   if (this.element) {
 3     this.element.style.top = aTop;
 4     this.element.style.left = aLeft;
 5   }
 6 }
 7 var objects = [];
 8 var createObject = function() {
 9   var obj = Math.random() > 0.5 ? new Box() : new Circle();
10   obj.create();
11   var top = (Math.random() * window.innerHeight).toString() + 'px';
12   var left = (Math.random() * window.innerWidth).toString() + 'px';
13   obj.moveTo(top, left);
14   objects.push(obj);
15   if (objects.length > 100) {
16     var i = Math.floor(Math.random() * objects.length);
17     objects[i].destroy();
18     objects.splice(i, 1);
19   }
20   setTimeout(createObject, 100);
21 }
22 setTimeout(createObject, 100);

2010-02-08 23:31

JavaScript

Latvian Keyboard Layout

Recently I got Nokia N900, but it doesn’t come with Latvian keyboard layout, not a big deal, since it’s running Linux and Xorg. This is a recollection of my journey into XKB configuration.

All of the fallowing code goes into /usr/share/X11/xkb/symbols/nokia_vndr/rx-51, after making changes, you run setxkbmap lv to activate the layout.

This was my first try:

 1 partial alphanumeric_keys modifier_keys
 2 xkb_symbols "lv" {
 3 
 4   include "nokia_vndr/rx-51(english_base)"
 5   include "nokia_vndr/rx-51(arrows_4btns)"
 6 
 7   name[Group1]= "Latvian";
 8 
 9   key <AD03> { [e, E, emacron, Emacron] };
10   key <AD07> { [u, U, umacron, Umacron] };
11   key <AD08> { [i, I, imacron, Imacron] };
12   key <AC01> { [a, A, amacron, Amacron] };
13   key <AC02> { [s, S, scaron, Scaron] };
14   key <AC05> { [g, G, gcedilla, Gcedilla] };
15   key <AC08> { [k, K, kcedilla, Kcedilla] };
16   key <AC09> { [l, L, lcedilla, Lcedilla] };
17   key <AB01> { [z, Z, zcaron, Zcaron] };
18   key <AB03> { [c, C, ccaron, Ccaron] };
19   key <AB06> { [n, N, ncedilla, Ncedilla] };
20 
21 };

This works as expected, you can type both Latin and Latvian letters, but the problem is that you lose some numbers and special characters, that are defined on the third level in english_base. Something like this would work perfectly, if you need to simply replace one letter with another. What’s interesting is that the fourth level isn’t actually necessary, it doesn’t get used. Looking at the modifiers symbols definition, you can see that there’s no way to switch to the fourth level, I don’t know why most keys have four levels defined, I did it just because that’s how Nokia does it. What’s interesting is that with three levels, you can get six different symbols:

 1 partial alphanumeric_keys modifier_keys
 2 xkb_symbols "lv" {
 3 
 4   include "nokia_vndr/rx-51(english_base)"
 5   include "nokia_vndr/rx-51(arrows_4btns)"
 6 
 7   name[Group1]= "Latvian";
 8 
 9   key <AD03> { [e, emacron, 3, 3] };
10   key <AD07> { [u, umacron, 7, 7] };
11   key <AD08> { [i, imacron, 8, 8] };
12   key <AC01> { [a, amacron, asterisk, asterisk] };
13   key <AC02> { [s, scaron, plus, plus] };
14   key <AC05> { [g, gcedilla, underscore, underscore] };
15   key <AC08> { [k, kcedilla, ampersand, ampersand] };
16   key <AC09> { [l, lcedilla, exclam, exclam] };
17   key <AB01> { [z, zcaron, sterling, sterling] };
18   key <AB03> { [c, ccaron, EuroSign, EuroSign] };
19   key <AB06> { [n, ncedilla, quotedbl, quotedbl] };
20 
21 };
That’s my second attempt, it works like this:
  • press e - level one - returns “e”
  • press Shift, then e - level one - returns “E”
  • hold Shift and press e - level two - returns “ē”
  • press Shift, then hold Shift and press e - level two - returns “Ē”
  • press or hold Fn, then e - level three - returns “3”
This is better than the first attempt, but very confusing to type, I don’t think you would want to use something like this, illustrates how the levels work though.

Another try:

 1 partial alphanumeric_keys modifier_keys
 2 xkb_symbols "lv" {
 3 
 4   include "nokia_vndr/rx-51(english_base)"
 5 
 6   name[Group1]= "Latvian";
 7 
 8   key <UP> { type[Group1] = "PC_FN_LEVEL2", symbols[Group1] = [Up] };
 9   key <LEFT> { type[Group1] = "PC_FN_LEVEL2", symbols[Group1] = [Left, dead_caron] };
10   key <DOWN> { type[Group1] = "PC_FN_LEVEL2", symbols[Group1] = [Down, dead_cedilla] };
11   key <RGHT> { type[Group1] = "PC_FN_LEVEL2", symbols[Group1] = [Right, dead_macron] };
12 
13 };

Here we set the second level of arrow keys to work as dead keys. I’m not sure why you needed to assign a special type, but this won’t work otherwise. If your language has one diacritic, this would be a good solution, more than that and this gets confusing.

Here’s the best and final configuration:

 1 partial alphanumeric_keys modifier_keys
 2 xkb_symbols "lv" {
 3 
 4   include "nokia_vndr/rx-51(english_base)"
 5   include "nokia_vndr/rx-51(arrows_4btns)"
 6 
 7   name[Group1] = "Latin";
 8   name[Group2] = "Latvian";
 9 
10   key <AD03> { symbols[Group2] = [emacron, Emacron, 3, 3] };
11   key <AD07> { symbols[Group2] = [umacron, Umacron, 7, 7] };
12   key <AD08> { symbols[Group2] = [imacron, Imacron, 8, 8] };
13   key <AC01> { symbols[Group2] = [amacron, Amacron, asterisk, asterisk] };
14   key <AC02> { symbols[Group2] = [scaron, Scaron, plus, plus] };
15   key <AC05> { symbols[Group2] = [gcedilla, Gcedilla, underscore, underscore] };
16   key <AC08> { symbols[Group2] = [kcedilla, Kcedilla, ampersand, ampersand] };
17   key <AC09> { symbols[Group2] = [lcedilla, Lcedilla, exclam, exclam] };
18   key <AB01> { symbols[Group2] = [zcaron, Zcaron, sterling, sterling] };
19   key <AB03> { symbols[Group2] = [ccaron, Ccaron, EuroSign, EuroSign] };
20   key <AB06> { symbols[Group2] = [ncedilla, Ncedilla, quotedbl, quotedbl] };
21 
22   key <AB08> { [ISO_Group_Latch, semicolon, equal, equal], [comma, semicolon, equal, equal] };
23   key <SPCE> { symbols[Group2] = [comma, space, at, at] };
24 
25 };

What this does is adds another group to keys and sets , key to “latch” onto it. Here’s how it works - every key works like normal, except ,, it sets key group to two, so that when you press the next key, that has a second group defined, a symbol from group two will be used, instead of the default first group. Works really well, I’m happy with this setup, only thing that could make it better, would be a dedicated Chr key, like some other Nokia phones have.

Another interesting possibility is to define keys with eight levels and set first group type to EIGHT_LEVEL_SEMIALPHABETIC, then you can switch between first four and last four levels with Ctrl + Space. Perfect for languages that don’t have Latin letters.

Resources: An Unreliable Guide to XKB Configuration, X Keyboard Extension - Иван Паскаль, example configurations.

2010-02-02 00:13

XKBMaemoN900

Computing MD5/SHA256/SHA512 Checksum

 1 using System;
 2 using System.Security.Cryptography;
 3 using System.Text;
 4 
 5 class Test {
 6 
 7   public static int Main(String[] args) {
 8     if (args.Length != 2) {
 9       return 1;
10     } else {
11       HashAlgorithm algo;
12       switch (args[0].ToUpper()) {
13         case "MD5":
14           algo = new MD5CryptoServiceProvider();
15           break;
16         case "SHA256":
17           algo = new SHA256CryptoServiceProvider();
18           break;
19         case "SHA512":
20           algo = new SHA512CryptoServiceProvider();
21           break;
22         default:
23           return 2;
24       }
25       Console.WriteLine(ComputeChecksum(algo, args[1]));
26       return 0;
27     }
28   }
29 
30   static public String ComputeChecksum(HashAlgorithm algo, String message) {
31     var bytes = Encoding.UTF8.GetBytes(message);
32     var digest = algo.ComputeHash(bytes);
33     var checksum = new StringBuilder();
34     foreach (Byte b in digest) {
35       checksum.Append(b.ToString("x2"));
36     }
37     return checksum.ToString();
38   }
39 
40 }

2010-01-24 22:24

.NETMonoC Sharp

sp_lock Replacement

Here’s what I’ve been using instead of sp_lock:

 1 SELECT
 2   l.request_session_id AS sid,
 3   r.status,
 4   d.name AS [database],
 5   o.name AS object,
 6   l.request_mode,
 7   l.request_status,
 8   r.blocking_session_id AS blocking_sid,
 9   r.wait_type,
10   r.wait_time,
11   r.total_elapsed_time,
12   r.percent_complete,
13   p.cpu,
14   p.physical_io,
15   p.memusage,
16   p.loginame,
17   p.hostname,
18   p.program_name,
19   t.text
20 FROM sys.dm_tran_locks AS l
21 INNER JOIN sys.databases AS d ON d.database_id = l.resource_database_id
22 INNER JOIN sys.objects AS o ON o.object_id = l.resource_associated_entity_id
23 LEFT JOIN sys.dm_exec_requests AS r ON r.session_id = l.request_session_id
24 LEFT JOIN sys.sysprocesses AS p ON p.spid = l.request_session_id
25 CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) AS t
26 WHERE
27   d.database_id = DB_ID() AND
28   l.resource_type = 'OBJECT' AND
29   r.session_id != @@SPID
30 ORDER BY
31   d.name,
32   l.request_session_id,
33   o.name

2010-01-17 04:21

SQLSQL Server

URL Mapping

Here’s full, working and as simple as possible example on how to get pretty URLs with .NET/Mono.

First, here’s all the code:

 1 using System;
 2 using System.Web;
 3 using System.Web.Routing;
 4 
 5 
 6 class RouteHandler : IRouteHandler {
 7 
 8   Type PageType;
 9 
10   public RouteHandler(Type pageType) {
11     PageType = pageType;
12   }
13 
14   public IHttpHandler GetHttpHandler(RequestContext requestContext) {
15     var routeData = requestContext.RouteData.Values.Values;
16     string[] args = new string[routeData.Count];
17     routeData.CopyTo(args, 0);
18     Page page = (Page)Activator.CreateInstance(PageType, args);
19     return page;
20   }
21 
22 }
23 
24 
25 public class Global : HttpApplication {
26 
27   protected void Application_Start() {
28     RouteCollection r = RouteTable.Routes;
29     r.Add(new Route("foo", new RouteHandler(typeof(Foo))));
30     r.Add(new Route("bar/{id}", new RouteHandler(typeof(Bar))));
31   }
32 
33 }
34 
35 
36 class Page : System.Web.UI.Page {
37 
38   override protected void OnLoadComplete(EventArgs e) {
39     if (Request.HttpMethod == "POST") {
40       Post();
41     } else {
42       Get();
43     }
44   }
45 
46   virtual protected void Get() {
47     throw new NotImplementedException();
48   }
49 
50   virtual protected void Post() {
51     throw new NotImplementedException();
52   }
53 
54 }
55 
56 
57 class Foo : Page {
58 
59   override protected void Get() {
60     Response.Write("FOO!");
61   }
62 
63 }
64 
65 
66 class Bar : Page {
67 
68   string Id;
69 
70   public Bar(string id) {
71     Id = id;
72   }
73 
74   override protected void Get() {
75     Response.Write("BAR" + Id + "!");
76   }
77 
78 }

Save that as global.cs and compile it with this, if you’re using Mono:

1 gmcs -debug+ -r:System.Web,System.Web.Routing -t:library global.cs

Or this, if you’re using .NET:

1 csc /debug+ /r:System.Web.Routing.dll /t:library global.cs

Drop the resulting global.dll in Bin directory on the web server. And to wire it up, you’ll need this global.asax:

1 <%@ Inherits="Global" %>

Also, your web.config should look something like this:

 1 <?xml version="1.0"?>
 2 <configuration>
 3   <system.web>
 4     <customErrors mode="Off" />
 5     <httpModules>
 6       <clear />
 7       <add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
 8     </httpModules>
 9   </system.web>
10 </configuration>

And that should be it! Try visiting /foo and /bar/123 on the web server. I successfully tested this with xsp2, lighttpd and IIS 6.0, just remember that, for this to work, web server needs to send all requests to CGI/ISAPI module, xsp2 always does this, in lighttpd, simply send all requests to fastcgi-mono-server, in IIS, send everything to aspnet_isapi.dll using “wildcard application maps” setting.

Type-Safe Collection of Diverse Types

So you have a couple of different objects (say, Foo and Bar), and would like to store them all in some sort of collection (Col), but in a type-safe way.

Here’s two solutions:

  1 using System;
  2 using System.Collections;
  3 using System.Collections.Generic;
  4 
  5 
  6 // required only for Col2
  7 public interface IItem {
  8 
  9   void Print();
 10 
 11 }
 12 
 13 
 14 public class Foo : IItem {
 15 
 16   public String Text;
 17 
 18   public Foo(String text) {
 19     Text = text;
 20     Print();
 21   }
 22 
 23   public void Print() {
 24     Console.Write("Foo:" + Text + "; ");
 25   }
 26 
 27 }
 28 
 29 
 30 public class Bar : IItem {
 31 
 32   public List<String> List;
 33 
 34   public Bar(List<String> list) {
 35     List = list;
 36     Print();
 37   }
 38 
 39   public void Print() {
 40     Console.Write("Bar:");
 41     foreach (String item in List) {
 42       Console.Write(item + ";");
 43     }
 44     Console.Write(" ");
 45   }
 46 
 47 }
 48 
 49 
 50 // stores objects as they are, without casting
 51 public class Col1 {
 52 
 53   private List<Foo> fooList = new List<Foo>();
 54 
 55   private List<Bar> barList = new List<Bar>();
 56 
 57   private List<Char> order = new List<Char>();
 58 
 59   public void Add(Foo item) {
 60     fooList.Add(item);
 61     order.Add('f');
 62   }
 63 
 64   public void Add(Bar item) {
 65     barList.Add(item);
 66     order.Add('b');
 67   }
 68 
 69   public void Print() {
 70     var fooEnu = fooList.GetEnumerator();
 71     var barEnu = barList.GetEnumerator();
 72     foreach (Char list in order) {
 73       switch (list) {
 74         case 'f':
 75           fooEnu.MoveNext();
 76           fooEnu.Current.Print();
 77           break;
 78         case 'b':
 79           barEnu.MoveNext();
 80           barEnu.Current.Print();
 81           break;
 82         default:
 83           throw new Exception();
 84       }
 85     }
 86   }
 87 
 88 }
 89 
 90 
 91 // stores objects cast to interface
 92 public class Col2 {
 93 
 94   private List<IItem> list = new List<IItem>();
 95 
 96   public void Add(IItem item) {
 97     list.Add(item);
 98   }
 99 
100   public void Print() {
101     foreach (IItem item in list) {
102       item.Print();
103     }
104   }
105 
106 }
107 
108 
109 // test collection with random data
110 public class Test {
111 
112   public static void Main() {
113     Col1 col = new Col1();
114     //Col2 col = new Col2();
115     Random rng = new Random();
116     for (Int16 i = 0; i < rng.Next(3, 10); i++) {
117       if (rng.Next(2) == 0) {
118         col.Add(new Foo(i.ToString()));
119       } else {
120         List<String> list = new List<String>();
121         for (Int16 i2 = 0; i2 < rng.Next(1, 4); i2++) {
122           list.Add(i.ToString() + i2.ToString());
123         }
124         col.Add(new Bar(list));
125       }
126     }
127     Console.WriteLine();
128     col.Print();
129     Console.WriteLine();
130   }
131 
132 }

2010-01-03 01:50

.NETMonoC Sharp

Restore Cursor Position

Add the fallowing line to your .vimrc and when a file is opened, the cursor will be positioned to the same line it was left on, when this file previously was closed.

1 autocmd BufReadPost * if line("'\"") > 0|if line("'\"") <= line("$")|exe("norm '\"")|else|exe "norm $"|endif|endif

2009-12-27 19:28

Vim

String Aggregation

Say you have the following data and you want to concatenate all queries for each year.

 1 CREATE TABLE years (year smallint);
 2 INSERT INTO years VALUES
 3   (2008),
 4   (2007),
 5   (2009);
 6 
 7 CREATE TABLE top_queries (year smallint, place smallint, query varchar(50));
 8 INSERT INTO top_queries VALUES
 9   (2009,  4, 'twitter'),
10   (2008,  2, 'beijing 2008'),
11   (2008,  9, 'euro 2008'),
12   (2007,  8, 'second life'),
13   (2008,  8, 'wer kennt wen'),
14   (2009,  6, 'new moon'),
15   (2007,  3, 'facebook'),
16   (2007,  4, 'dailymotion'),
17   (2009,  8, 'windows 7'),
18   (2007,  7, 'ebuddy'),
19   (2009,  9, 'dantri.com.vn'),
20   (2009, 10, 'torpedo gratis'),
21   (2009,  3, 'tuenti'),
22   (2008,  1, 'sarah palin'),
23   (2008,  6, 'obama'),
24   (2009,  1, 'michael jackson'),
25   (2007,  1, 'iphone'),
26   (2009,  5, 'sanalika'),
27   (2007, 10, 'club penguin'),
28   (2007,  9, 'hi5'),
29   (2009,  2, 'facebook'),
30   (2008,  4, 'tuenti'),
31   (2007,  6, 'youtube'),
32   (2008,  7, 'nasza klasa'),
33   (2008,  3, 'facebook login'),
34   (2009,  7, 'lady gaga'),
35   (2007,  5, 'webkinz'),
36   (2007,  2, 'badoo'),
37   (2008,  5, 'heath ledger'),
38   (2008, 10, 'jonas brothers');

For SQL Server, a recursive CTE is good option:

 1 WITH
 2   q AS (
 3     SELECT
 4       ROW_NUMBER() OVER(PARTITION BY year ORDER BY place) AS nr,
 5       year,
 6       query
 7     FROM top_queries
 8   ),
 9   t AS (
10     SELECT
11       year,
12       1 AS query_nr,
13       CONVERT(varchar(8000), '') AS queries
14     FROM years
15     UNION ALL
16     SELECT
17       t.year,
18       t.query_nr + 1,
19       CONVERT(varchar(8000),
20         t.queries +
21         CASE WHEN LEN(t.queries) > 0 THEN ', ' ELSE '' END +
22         q.query
23       )
24     FROM t
25     INNER JOIN q ON
26       q.year = t.year AND
27       q.nr = t.query_nr
28   )
29 SELECT
30   year,
31   queries
32 FROM t
33 WHERE
34   query_nr IN (
35     SELECT MAX(nr) + 1 FROM q GROUP BY year
36   )
37 ORDER BY year;

Or maybe, although I never understood how exactly this works, FOR XML clause:

 1 SELECT
 2   year,
 3   LEFT(queries, LEN(queries) - 2) AS queries
 4 FROM (
 5   SELECT
 6     year,
 7     (
 8       SELECT q.query + ', ' AS [text()]
 9       FROM top_queries AS q
10       WHERE q.year = y.year
11       ORDER BY q.place
12       FOR XML PATH('')
13     ) AS queries
14   FROM years AS y
15   GROUP BY year
16 ) AS t
17 ORDER BY year;

In PostgreSQL, pretty much the same CTE query would work, but you’re better off with array_agg function:

1 SELECT
2   y.year,
3   array_to_string(array_agg(query), ', ') AS queries
4 FROM (
5   SELECT * FROM top_queries ORDER BY place DESC
6 ) AS q
7 INNER JOIN years AS y ON y.year = q.year
8 GROUP BY y.year
9 ORDER BY y.year;

Autocompletion

Vim has quite a few features for automatic text completion. The most basic is keyword completion - 1) open a file, 2) write first few characters of any word you see in the file, 3) hit CTRL+N to automatically complete the word. Simple, easy to use and very useful.

To make this feature more intuitive, consider mapping CTRL+Space to CTRL+N:

1 inoremap <C-Space> <C-n>
2 inoremap <C-S-Space> <C-p>

2009-12-13 21:30

Vim

Generating a Range of Values

For example, lets generate a set of months, from 2010-01-01 to 2011-01-01.

In SQL Server:

1 WITH t AS (
2   SELECT CONVERT(date, '2010-01-01') AS d
3   UNION ALL
4   SELECT DATEADD(MONTH, 1, d) FROM t WHERE d < '2010-12-01'
5 )
6 SELECT * FROM t ORDER BY d;

Same in PostgreSQL:

1 WITH RECURSIVE t AS (
2   SELECT '2010-01-01'::timestamp AS d
3   UNION ALL
4   SELECT d + '1 months' FROM t WHERE d < '2010-12-01'
5 )
6 SELECT * FROM t ORDER BY d

In PostgreSQL, you can also use generate_series() function, but it’s useful only in these basic cases:

1 SELECT * FROM generate_series('2010-01-01'::timestamp, '2010-12-01', '1 months');
| Archive | RSS | E-mail | Twitter | Alvis Mikovs