(m)Ruby + PostgreSQL = <3
Holycorn makes it easy to implement a Foreign Data Wrapper using Ruby.
It is based on top of mruby, that provides sandboxing capabilities the regular Ruby VM "MRI/CRuby" does not provide.
- PostgreSQL 9.1+
Clone and build mruby
inside the vendor directory
cd vendor
git clone https://github.com/mruby/mruby.git
cd mruby
make
cd ../..
Building holycorn
is as simple as
make
and installing it only requires oneself to
make install
Now you can start setting up the Foreign Data Wrapper:
DROP EXTENSION holycorn CASCADE;
CREATE EXTENSION holycorn;
CREATE SERVER holycorn_server FOREIGN DATA WRAPPER holycorn;
CREATE FOREIGN TABLE holytable (some_date timestampz) \
SERVER holycorn_server
OPTIONS (wrapper_path '/tmp/source.rb');
And the source file of the wrapper:
# /tmp/source.rb
class Producer
def initialize(env = {}) # env contains informations provided by Holycorn
end
def each
@enum ||= Enumerator.new do |y|
10.times do |t|
y.yield [ Time.now ]
end
end
@enum.next
end
self
end
Now you can select data out of the wrapper:
λ psql
psql (9.4.1)
Type "help" for help.
franck=# SELECT * FROM holytable;
some_date
---------------------
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
2015-06-21 22:39:24
(10 rows)
Pretty neat.
Any type of Ruby object can act as a FDW. The only requirements are that it can
receive .new
(with arity = 1) and return an object that can receive each
(arity = 0).
It doesn't have to be a Class
, and there's currently no will to provide a
superclass to be inherited from.
In future versions, there will be many more callbacks to interact with PG's FDW
infrastructure through Holycorn
.
Also, the script can only be a single word - like MyClass
- as long as
MyClass
has been defined and exists within your compilation of mruby
.
A hash is passed by Holycorn
to the Ruby script. Its current keys are:
PG_VERSION
PG_VERSION_NUM
PACKAGE_STRING
PACKAGE_VERSION
MRUBY_RUBY_VERSION
WRAPPER_PATH
MRB_TT_FREE
=>null
MRB_TT_FALSE
=>Boolean
MRB_TT_TRUE
=>Boolean
MRB_TT_FIXNUM
=>Int64
MRB_TT_SYMBOL
=>Text
MRB_TT_UNDEF
=> UnsupportedMRB_TT_FLOAT
=>Float8
MRB_TT_CPTR
=> UnsupportedMRB_TT_OBJECT
=>Text
(to_s
is called)MRB_TT_CLASS
=>Text
(class.to_s
is called)MRB_TT_MODULE
=>Text
(to_s
is called)MRB_TT_ICLASS
=> UnsupportedMRB_TT_SCLASS
=> UnsupportedMRB_TT_PROC
=>Text
(inspect
is called)MRB_TT_ARRAY
=>Text
(inspect
is called)MRB_TT_HASH
=>Text
(inspect
is called)MRB_TT_STRING
=>Text
MRB_TT_RANGE
=>Text
(inspect
is called)MRB_TT_EXCEPTION
=> UnsupportedMRB_TT_FILE
=> UnsupportedMRB_TT_ENV
=> UnsupportedMRB_TT_DATA
=> See "Arbitraty Ruby objects" sectionMRB_TT_FIBER
=>Text
(inspect
is called)MRB_TT_MAXDEFINE
=> Unsupported
- Time (Ruby) =>
timestampz
TBD;
TBD;
- Array type
- JSON type
- Range type
- Support PG 9.5's
IMPORT FOREIGN SCHEMA
for easy setup
- Fork the project.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don't break it in a future version unintentionally.
- Commit, do not mess with version or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
- Send me a pull request. Bonus points for topic branches.
Copyright (c) 2015 Franck Verrot. MIT LICENSE. See LICENSE.md for details.