I admit it. I have had a torrid love affair with procedural languages
ever since I started playing with PostgreSQL. The ability to share logic
amongst all the applications touching the same database server..
wasâŚwell⌠a breath of fresh air.
What is a procedural language in Postgresql?
PostgreSQL docs describe them as, ââŚallows user-defined functions to
be written in other languages besides SQL and C. â
Well, PostgreSQL has PLs for Perl, Python, Java, C, PHP⌠and even
RUBY!
CREATE FUNCTION ruby_max(int4, int4) RETURNS int4 AS
' if args[0].to_i > args[1].to_i return args[0] else return
args[1] end ' LANGUAGE 'plruby';
PL/PGSQL is nice and all, but itâs not as fun as playing with Ruby.
PL/Perl⌠well is perl, and PL/Python⌠is python. Both PL/Perl and
PL/Python have untrusted variants. You see, they donât want your
PostgreSQL server to do anything harmful to the machine by being able to
do stuff like system(âcat /dev/null > /etc/passwd). But for some
people, (like me) they want the flexibility of their language anyways.
:-)
Note: Never do this if your system user that runs PostgreSQL has
privileges to do anything harmful on your system.
The PL/Ruby documentation is minimal at the moment, but covers enough to
get you started. I donât know if many people are using it out thereâŚ
but hopefully that is about to change! Iâve played with it a bit, but
always wanted to be able to do stuff like require ârubygemsâ, but this
is a feature of an untrusted language. I even found myself digging
around in C code to see if I could figure out how to hack the plruby
language to skip over those checks⌠but I am not a C programmer and I
got lost in some header files.
Then it hit me. âWhy havenât you emailed the author?â
So I emailed the author of PL/Ruby, Guy Decoux, who responded pretty
quickly with the answer to my dreams! Okay, I do have bigger dreams than
this⌠but you get the idea.
First of all, some of you might be thinking, âWhy on Earth would you
want to do this?â
Well, here is a simple example of how it could be used with RedCloth
Letâs say that I want to be able to perform the following query from
within SQL.
SELECT redcloth('**strong text** and *emphasized text*');
Why not do this in the application? Well, I do actually have a case
where I have an older PHP application that I will be porting to Ruby in
the future, but would like to give the application some access to some
of the features of Ruby that I will be using, such as RedCloth.
Okay, so show me an example of one of these scary PostgreSQL functions.
CREATE FUNCTION redcloth(text) RETURNS text AS '
require "rubygems"
require "redcloth"
content = args[0]
rc = RedCloth.new(content)
return rc.to_html
' LANGUAGE 'plruby';
âWait! You said this would be scary!?â
Well, PL/Ruby allows you to write⌠plain ole Ruby within your
functions. (do you see where I am getting here?)
PL/Ruby meets RedCloth
rb=# SELECT redcloth('*strong text* and _emphasized text_');
redcloth
------------------------------------------------------------------
<p><strong>strong text</strong> and <em>emphasized text</em></p>
(1 row)
PL/Ruby meets ShortURL
CREATE FUNCTION rubyurlize(text) RETURNS text AS '
require "rubygems"
require "shorturl"
return ShortURL.shorten(args[0])
' LANGUAGE 'plruby';
âŚwhich allows for
rb=# SELECT
rb-# rubyurlize('http://www.robbyonrails.com/') as link1,
rb-# rubyurlize('http://moulon.inra.fr/ruby/plruby.html') as link2;
link1 | link2
--------------------------+------------------------
http://rubyurl.com/lyoKm | http://rubyurl.com/dTo
(1 row)
PostgreSQL meets DRb
Okay, this is one of the reasons why I wanted to play with PL/Ruby a bit
more. Distributed Ruby Objects⌠from PostreSQL?
What is DRb?
If you donât know already⌠per the description in RDOC, âdRuby is a
distributed object system for Ruby. It allows an object in one Ruby
process to invoke methods on an object in another Ruby process on the
same or a different machine.â
It basically allows you to share an object to other machines⌠at the
same time!
mmmâŚdistributed objectsâŚ
DRb Object
Here is a simple ruby script that you would run from the shell. It
creates a DRb object which accepts connections at localhost:9000.
#!/usr/bin/ruby
require 'drb'
class MyRemoteObject
def say(str)
return "You say #{str}. I say #{str.reverse.upcase}!"
end
end
server = MyRemoteObject.new
DRb.start_service('druby://localhost:9000', server)
DRb.thread.join
Start me up!
Now that we have DRb running and listening for connectionsâŚwe need a
client to connect to it.
DRb function in PL/Ruby
Here is a very simple DRb client script and I just drop that into a
PostgreSQL function.
CREATE FUNCTION drb_test(text) RETURNS text AS '
require "drb"
DRb.start_service
ro = DRbObject.new(nil, "druby://localhost:9000")
return ro.say(args[0])
' LANGUAGE 'plruby';
The result?
rb=# SELECT drb_test('Potato');
drb_test
-------------------------------
You say Potato. I say OTATOP!
(1 row)
Are we having fun yet?
Okay, so how do I manage to get this to work? Well⌠for that, you will
have to read my blog post, Installing untrusted PL/Ruby for PostgreSQL
Letâs all go get some coffee (or tea) and start playing with PL/Ruby
today!