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';
````ruby
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.
```sql
SELECT redcloth('**strong text** and *emphasized text*');
```bash
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.
```sql
CREATE FUNCTION redcloth(text) RETURNS text AS '
require "rubygems"
require "redcloth"
content = args[0]
rc = RedCloth.new(content)
return rc.to_html
' LANGUAGE 'plruby';
```shell
"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
```sql
rb=# SELECT redcloth('*strong text* and _emphasized text_');
```text
redcloth
```yaml
------------------------------------------------------------------
<p><strong>strong text</strong> and <em>emphasized text</em></p>
(1 row)
```shell
## PL/Ruby meets ShortURL
```sql
CREATE FUNCTION rubyurlize(text) RETURNS text AS '
require "rubygems"
require "shorturl"
return ShortURL.shorten(args[0])
' LANGUAGE 'plruby';
```text
...which allows for
```sql
rb=# SELECT
rb-# rubyurlize('http://www.robbyonrails.com/') as link1,
rb-# rubyurlize('http://moulon.inra.fr/ruby/plruby.html') as link2;
```text
link1 | link2
```ruby
--------------------------+------------------------
http://rubyurl.com/lyoKm | http://rubyurl.com/dTo
(1 row)
```shell
## 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.
````ruby
#!/usr/bin/ruby
require 'drb'
class MyRemoteObject
def say(str)
```text
return "You say #{str}. I say #{str.reverse.upcase}!"
```ruby
end
end
server = MyRemoteObject.new
DRb.start_service('druby://localhost:9000', server)
DRb.thread.join
```text
Start me up!
```bash
$ ruby mydrb.rb
```text
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.
```sql
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';
```text
The result?
```sql
rb=# SELECT drb_test('Potato');
```text
drb_test
```yaml
-------------------------------
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!