Friday, November 21, 2008

Perl references to return values

Today I was working on some Perl code that needs to take an array returned from one function, make a reference to that array, and pass the reference to another function. It seemed like a simple enough thing to do. I've been doing Perl programming for seven years. After trying a few things and hitting failure, I pulled out the O'Reilly book Programming Perl and read the chapters on references and on subroutines. You can make references to anything: scalars, arrays, file handles, subroutines. But it seemed you can't just take a sub-routine's return value and make a reference to it.

A little poking around on Google turned up an indication that I'm not missing something. There may not be a way to do exactly what I'm trying to do according to a post on Perlmonks.org. So instead of making a reference to a sub-routine's return value, you have to make a reference to a copy of the return value.

Here is the code that demonstrates this:

#!/usr/bin/perl
#From a post on caffeinemakesmesleepy.blogspot.com
# Demonstrate how to take an array return from one function and pass a reference to it into another.

#[] around return_array function makes a reference to a copy of the return value of that function.
# you need the () after return_array or "return_array" gets passed into take_array_ref
take_array_ref([return_array()]);

sub return_array {
my @ret;
push (@ret, 'FOO');
push (@ret, 'BAR');
return @ret;
}

sub take_array_ref {
my $ref = shift;
my $i = 0;
foreach my $value (@$ref) {
$i++;
print "Element #$i: $value\n";
}
}

Saturday, October 25, 2008

Perl Test::More trick

Recently I was working on a rather large Perl module and writing unit tests for it. I had some tests already completely done and trying to debug another chunk of code & tests. It was hard with debugging on to see what was going on. I just wanted to see one set of tests and nothing from the other sets. At first I commented out the blocks of code that ran the tests I didn't care about. Then I realized there was an easier way.

Say you have a test that starts with something like:

sub my_batch_of_tests : Test(6) {


Easy way to disable these: turn the above line to something like

sub my_batch_of_tests { # : Test(6)


When you do run_tests() on this test module, it won't run this block of tests because this function will no longer register as a test.


NOTE: this tip assumes you are using Test::More and Test::Class and have it set up so all your tests are in a module and you have a script that loads that module and does ->run_tests on the module with your tests.

Wednesday, October 1, 2008

SQL - you can't compare NULL to anything

Here is a little mind-bender for any of you who are programmers who routinely work with SQL. Write a little code that checks to see if NULL equals NULL. It returns false. Now write code to see if NULL is not equal NULL. It will return false too. Now repeat with NULL and any value. NULL equals 42 returns false, NULL is not equal 42 returns false. * What in the world is going on here?

Here's what I found when I ran into this issue before. What we're seeing here is the impact of NULL being a mathematical concept. SQL is built on many mathematical concepts. The concept of NULL is an unknown value. Since NULL is an unknown value, it may be 42, it may be Fred. We simply don't know. So comparing anything at all to NULL (even comparing NULL to itself) will *ALWAYS* return false. This is why you need to specify IS NULL or IS NOT NULL if you want to see if something is or isn't NULL.

* I experienced this issue most recently working with Oracle, but it will work with any SQL database such as MySQL, PostgreSQL, and Microsoft SQL Server.

Friday, September 5, 2008

Using Windows environment variables

This post will be a little more basic than most things I write here. I'm used to using MacOS/X on the desktop and Linux on the server side. So when I have to use Windows for one reason or another it sometimes takes me a bit to figure things out.

Today I was helping a co-worker with Oracle Application Express (APEX for short) and the install directions said to run "cd $ORACLE_HOME/apex" and then run some commands. The cd command didn't work because Windows does environment variables different than Unix operating systems.

This article covers it in more detail but in short Windows indicates environment variables by putting a % (percent sign) before and after the variable. Variables are not case sensitive.

You can show them by running "echo %ENV_NAME%" and set them on the command line by running "ENV_NAME = new_value". You can make the setting part of your login profile by right-clicking on "My Computer", then click Properties, then Advanced, and then Environment Variables. This is for XP, the location may be different on Vista or other versions of Windows.

Saturday, June 7, 2008

MySQL++ meets Leopard on an Intel Mac

A friend of mine is learning C++ and also wants to learn MySQL. I decided to go ahead and show him how to connect MySQL to a database in C++. Once I started, I realized I've never done this with MySQL, only with PostgreSQL. Oh well, how different could it be???

I decided to install on my Mac Mini which is running MacOS/X 10.5 Leopard. Installing the latest stable version of MySQL from source was cake. I got the sample C program working with no problems and then I looked for how to hookup C++ to MySQL. MySQL itself does not come with a C++ API, only C. The C++ bindings are provided by a project called MySQL++. This is when the problems started.

First off, it gave me some errors about not being able to find some functions. This was the actual error:

./lib/mystring.cpp: In member function ‘int mysqlpp::String::compare(const std::string&) const’:
./lib/mystring.cpp:67: error: no matching function for call to ‘max(unsigned int, size_t)’
./lib/mystring.cpp: In member function ‘int mysqlpp::String::compare(const char*) const’:
./lib/mystring.cpp:81: error: no matching function for call to ‘max(unsigned int, size_t)’
./lib/mystring.cpp: In member function ‘int mysqlpp::String::compare(const std::string&) const’:
./lib/mystring.cpp:67: error: no matching function for call to ‘max(unsigned int, size_t)’
./lib/mystring.cpp: In member function ‘int mysqlpp::String::compare(const char*) const’:
./lib/mystring.cpp:81: error: no matching function for call to ‘max(unsigned int, size_t)’


A little googling around turns up this message on the MySQL++ mailing list. Apparently the way things were coded, this "max" function works perfectly fine on 32 bit processors, but not 64 bit. The fix is to change 1 line of the lib/mystring.h file in the source from "typedef unsigned int size_type;" to "typedef size_t size_type;". It sounds like this fix will be in the next release of MySQL++ and then no one will have to worry about it.

Another note here - the library for MySQL++ isn't libmysql++ or mysql++ as one may expect. It is mysqlpp. Just as well, special characters tend to mess things up sometimes.

The other problem I ran into was this error when trying to compile the sample program from the MySQL++ documentation:

ld: in /usr/local/lib, can't map file, errno=22
collect2: ld returned 1 exit status


This turned out to be one of those silly mistakes that causes a big problem. In specified the linker path, I had put

-L /usr/local/lib

When I changed it to

-L/usr/local/lib

it worked fine. This seems like something the GNU development utilities should be able to be smart enough to handle.

Thursday, June 5, 2008

Tortoise SVN for Windows tips

I just figured out two new features in Tortoise SVN today. First off, you can get SVN info showing up in Windows Explorer.

1. Go to a folder that contains files you checked out from SVN. Go to View -> Details
2. Right click on any column to get a list of available columns.
3. Select "More" and scroll down until you get to the list of "SVN" details. I highly recommend SVN Status & SVN Revision.
4. Click OK and then refresh the window. You'll have SVN status & revision # on all the files in the folder controlled by SVN.

5. (Optional) Go to Tools -> Folder Options and click the View tab. Hit Apply to All Folders & you'll have this same view for all folders next time you open them.

Trick #2.
1. Open up TortoiseSVN settings & go to "General".
2. Click on the "Set filedates to the "last commit time"
3. Hit Apply & then OK.

Your files from SVN will now show the last time you committed them as the modification time. One note here: this didn't seem to work for me until I deleted my files from my SVN working copies & checked them out again.

Friday, March 7, 2008

Ruby exceptions vs. logging

A while ago, I ran into a little problem with some software. We had some Ruby scripts running from cron. Going by the logs, they would start up and just go to the bit bucket never to be heard from again. After checking the code to try to figure out what was going wrong, I decided to just run them by hand to see what would happen.

BLAM!!! A nice big unhandled exception, which of course in Ruby makes whatever script you are running exploded in a rather show-stopping manner. Why wasn't this logged?

It turns out that this is all in how an exception actually works. If you don't handle an exception, the program keeps going up the call stack (function A calls function B, etc) until it finds something that can handle the exception. If nothing handles the exception, the program just stops right then & there and your exception becomes a fatal error.

This problem is possible in multiple scripts, and we didn't have time to rewrite them all the catch all possible exceptions, which sounds too time consuming anyway.

Here's the solution I ended up with.

It turns out in Ruby when a program exits normally or because of an unhandled exception, it sends the EXIT signal. So all you have to do is catch the EXIT signal and log any exceptions that come across.

This is one time that Ruby shows its Perl influence. The $! variable is whatever exception is present. So, trap the signal and check $! for the exception (aka the error).

There is one final twist on this, and it turns out a normally running Ruby program relies on exception handling, even if you don't know you're using exceptions.

If you do a call to the exit() function in your code, that actually throws a SystemExit exception, which causes Ruby to immediately stop your program.

So this is the code I ended up putting in our logger object, which is just an extension of the standard Ruby logger:


#Catch & log unhandled exceptions
Signal.trap("EXIT") {
unless $!.class.name == "SystemExit" || $!.nil?
self.fatal("Unhandled exception: #{$!.class.name}: #{$!.to_s}")
end
}


The log entries would end up looking something like:

Unhandled exception: FooException: Something went terribly wrong in the get_important_data method and the program cannot continue.


In retrospect, there are probably two better ways this could be done.

1. Make each program have an "execute" method of some sort that kicks off all the logic for the rest of the program. You could then wrap exception handling code around the call to "execute" and this would grab all your exceptions that aren't handled elsewhere in your code.

2. Assuming these are all scripts of some sort, likely to be run from cron, you could have a script that runs scripts. It could log anything that goes to standard output as "info" messages, standard error as "warning" messages, and any script that exits with a non-zero status (meaning an error) could be logged as a fatal error.

Tuesday, February 26, 2008

how to cheat with ruby gems

It's nice to have a quick reference of commands for whatever technology you are working with. I noticed a Capistrano cheatsheet hanging up in a coworkers cubicle today. I think it was the one that used to be available at this link: http://weblog.rubyonrails.org/2006/9/30/capistrano-cheat-sheet but it now seems to result in a Rails application error.

I went looking for a mirror of it and ended up finding something I like better. Ruby developers at ErrFree have set up a great gem called "cheat". More info on the cheat page. Short version:

You can install the "cheat" gem with "sudo gem install cheat" which works out of the box on MacOS/X Leopard. This will allow you to snag & view cheatsheets, which are downloaded into .cheats/ in your home directory.

To see how to use cheat run:
cheat cheat


Or to view any cheat simple run
cheat [cheatname]


And if you want to snag the latest version just run
cheat --new


The cheats are served up from a lightweight wiki, which you can add to and update. No usernames or passwords, just a kaptcha to keep spammers out. I added an entry to see if it was as easy as it looked. The Oracle cheat now contains


See current time:

SELECT SYSDATE FROM DUAL;

Monday, January 21, 2008

X11 on MacOS/X 10.5 (aka Leopard)

Being a Linux geek who moved to Mac for a desktop, I still do run X11 applications on occasion and wanted to get some of the applications available in Linux working on my Mac. There are two ways to do this:

MacPorts (previously known as Darwin Ports).


Fink

Mac Ports is based on the FreeBSD "ports" package management system that installs software by fetching the source and compiling it.

Fink is a system based on the Debian package management tools (apt-get) and will feel quite familiar to anyone who has used Debian-based Linux distributions such as Ubuntu.

I've had better luck with Mac Ports and installed a light-weight X11 window manager, blackbox, with the following command:


port install blackbox


I then tried to make it run by creating the following file in my home directory as the file ".xinitrc" and running X11 from the /Applications/Utilities/ folder:


exec blackbox


This resulted in X11 starting up and immediately quitting. I investigated using the Console.app and saw the following error message:

1/21/08 3:32:33 PM org.x.X11[88661] blackbox: another window manager is already running on display ':0.0'

A little searching online and I found this article on X11 on the Fink project site. So - Apple runs their own window manager and you have to tell it not to run if you want to run something else. If you're not using Fink, you'll have to leave out the Fink-specific parts (anything that starts with /sw/).

Here's the .xinitrc file that successfully starts up the blackbox window manager on Leopard:

PATH="$PATH:/opt/local/bin"
quartz-wm --only-proxy &
exec blackbox

puts "Hello world!"

Q: Another blog? Why?

A: There are multiple technologies I'm wanting to learn and learn better. Rather than to keep all the knowledge to myself, I decided to share what I learn.

Q: What's up with the blog's name?

A: It really does seem like if I consume too much caffeine, it does make me sleepy. It sounded like a good name for a blog about programming and technology.

Q: What will this blog focus on?

A: Whatever technology I feel like learning at the time. Here's some things I'm wanting to get into: Solaris, Oracle, Ruby on Rails, mod_perl, vim, and MacOS/X.