Six years ago, I did not know PHP, but I did know C, COM, and JNI, and that was enough for me to “port” the existing PHP4 COM interface to support Java. In the process, I learned a bit about PHP.
Now, I’m in a similar situation. The current state of IBM DB2 support in Rails is somewhat... suboptimal. Unfortunately, I don’t know enough of DB2 to be able to help much with that. However, I do know enough of C, PHP, and Ruby to be able to port the PHP ibm_db2 support to Ruby.
Better yet, this time I have a set of test cases to start with.
At the moment, there are 118 tests defined. Porting them from PHP to Ruby would be tedious and error prone. Here’s where a script could help. This script doesn’t try to be a general purpose PHP to Ruby translator, instead it simply focuses on the idioms that are found in the existing phpt files.
Accompanying the script is an embedded ruby template for the resultant ruby code.
Finally, there is a simple
test
driver which defines what tests need to be run (note that
I’m currently skipping test 002, and stopping on test
003). It also contains an initialize
function
with userid/password stuff, a setup
function which
mirrors the existing
connection.inc and
PHP prepare.inc files. Finally, there is a one line
expect
method which extracts the expected results from
the end of each test file
(phpt style).
You can peruse the current output of these tests here. The first few tests have been verified, the rest may be garbage for all I know. Over time, the set of tests to be run will be expanded; tests which are too difficult to automatically convert will simply be excluded in the first pass, and the accumulated set of excluded tests will effectively become a TODO list for a second pass.
Like PHP, Ruby provides for writing extensions.
The first step is to write an extconf.rb, which serves a similar purpose as the config.m4 file.
Next, there is the ibm_db2.c and ruby_ibm_db2.h files which were directly copied from the php pecl/ibm_db2 directory. Generally, the structure has been retained, just a number of Zend/PHP’isms have been converted into Rubyisms.
I don’t know if this will result in an ‘ideal’ Ruby API — it certainly isn’t object oriented. I just know that this API is the one that I have a ready set of unit tests for, and presumably most people will use it hidden behind an ActiveRecord interface anyway, so it may not much matter. Furthermore, the closer this code is to the PHP extension, the more likely that there will be people who can help contribute to it.
At the present time, about 70% of the code is
#ifdef
'ed out, awaiting to be re-added as unit tests
require.
Pulling it all together is a Rakefile (rake being Ruby’s answer to make). If you have a standard installation, this file will take care of everything from generating a Makefile, compiling and linking the shared library, generating tests, and even running the tests. Simply change whatever files you like and execute
rake
James Snell helped me retrace my steps. If you are interested in taking a look at this code, first you need to install Ruby and DB2.
cvs -d :pserver:cvsread@cvs.php.net:/repository checkout pecl/ibm_db2/tests sudo apt-get install ruby1.8-dev db2sampl wget http://intertwingly.net/stories/2005/12/05/rubydb2.tgz tar xzf rubydb2.tgz cd rubydb2
You will need to edit lines 17 and/or 18 in test.rb, and line 7
in php2ruby.rb. Then simply execute rake
.
You should see
2 tests, 2 assertions, 0 failures, 0 errors
Further development is as simple as incrementing the point at
which the tests.rb stops, executing rake
, observing
the failure, making a fix to the test generator and/or the shared
library, repeating until all failures go away, and then going back
to increment the break point in tests.rb and repeating the whole
process over.
There’s probably plenty of cleanup to be done. I
know I am making too many copies of strings. Where the PHP
implementation produced messages and return codes, the Ruby
implementation produces exceptions. It would be nice if
Connection and Statement objects weren’t simply opaque
handles, but had methods defined on them — at a minimum a
customized to_s
method would be nice. Things
like php.ini probably need to handled as singleton attributes on
the DB2 class instead.
But most of all, the point here is to build something that others can contribute to and maintain.
Down the road, it might make sense to explore a common extension
API. Most of the changes I have been making are fairly
mechanical, replacing zend_parse_parameters
with
rb_scan_args
, ZEND_FETCH_RESOURCE2
with
Data_Get_Struct
and the like. Perhaps a unified
set of macros could handle 80% of the work, with the remaining to
be included in appropriately #ifdef
'ed sections.
Wouldn’t using some tool like SWIG yield faster results?
I don’t think so in this case, given that the code involved seems to be quite a bit more than a simple API. The code implements persistent connections and dynamic mapping of SQL results - something somewhat beyond what I think that SWIG can handle.
What am I missing?
As far as I know I followed all the steps given..
Thanks,
Anbu
/usr/bin/ruby18 tests.rb (in /tmp/rubydb2) tests.rb:18:in `define_method': #<YAML::Syck::Scalar:0xb692fa7c> is not a symbol (TypeError) from tests.rb:18 from tests.rb:17 rake aborted! Command failed with status (1): [/usr/bin/ruby18 tests.rb] ./Rakefile:23
Anbu: I’m not sure why YAML behaves differently on your system than on mine, but we should be able to accommodate both by changing line 18 in tests.rb from
define_method(name) {node.value}
to
define_method(name.respond_to? :value ? name.value : name) {node.value}
If you have further problems, let me know and I will try to debug them.
P.S.: I’d also suggest that you move up to the latest code.
Appreciate your prompt response. I am running the latest code.
I tried changing tests.rb as you suggested. It still didn’t work.
Output:
/tmp/rubydb2/tests.rb:18: warning: parenthesize argument(s) for future version false is not a symbol /tmp/rubydb2/tests.rb:18:in `define_method' /tmp/rubydb2/tests.rb:18 /tmp/rubydb2/tests.rb:17 /tmp/rubydb2/tests.rb:18:in `define_method': false is not a symbol (TypeError) from /tmp/rubydb2/tests.rb:18 from /tmp/rubydb2/tests.rb:17
I tried debugging a bit(with my little ruby knowledge).
configdata['connection'].children_with_index.each {|node,name| puts "node =>"+node.to_s puts "name =>"+name.value.to_s define_method(name.respond_to? :value ? name.value : name) {node.value} }
gave
/tmp/rubydb2/tests.rb:20: warning: parenthesize argument(s) for future version node => name =>database false is not a symbol /tmp/rubydb2/tests.rb:20:in `define_method' /tmp/rubydb2/tests.rb:20 /tmp/rubydb2/tests.rb:17 /tmp/rubydb2/tests.rb:20:in `define_method': false is not a symbol (TypeError) from /tmp/rubydb2/tests.rb:20 from /tmp/rubydb2/tests.rb:17
I am surprised to see node being nil
This is my config.yml
source: pecl_dir: ../pecl connection: database: SAMPLE user: db2inst1 password: db2inst1 hostname: localhost port: 50000
m/c details..
anbu@GENTOO ~ $ ruby --version ruby 1.8.3 (2005-09-21) [i686-linux] anbu@GENTOO ~ $ uname -a Linux GENTOO 2.6.13-gentoo-r3 #3 Sun Oct 9 07:00:11 EST 2005 i686 AMD Athlon(tm) XP 3000+ AuthenticAMD GNU/Linux
Try:
ruby -r yaml -e 'p YAML::Syck::VERSION'
I was getting 0.45, but upgrading to 0.55, I see that a number of interfaces have changed. Replacing module Config in tests.rb with the following works for me:
module Config require 'yaml' configdata = open('config.yml') {|f| YAML::load f} configdata['connection'].each {|name,value| define_method(name) {value}} alias db database alias username user end
I worked with the IBM DB2 product and C370 on MVS about ten years ago. We wrote API code used by the applications development team in C to access DB2 databases.
Due to high transaction volume against database tables in the shop I worked in, a leading RDBMS was abandoned, and DB2 was brought in to save the large, multi-year project.
I am convinced that DB2’s optimizer technology is the best, year-on-year, in the industry. This fact, and the scaleability of DB2, explains why the product persists.
I am new to Ruby, but see it’s appeal, and was interested in getting involved with this project of making DB2 on LINUX available, using Ruby as a host language.
I can be contacted at greg_w_neely@att.net, or greg_w_neely@yahoo.com