A Cautionary Tale About Primary Keys:
Once upon a time . . . there was a developer named "Bryan", who was assigned the task of creating a relational database application that tracked the relationships between buildings and the people assigned to them. It was an easy enough application, with only two tables-- People and Sites. When creating the Sites table, "Bryan" thought it would be okay to use the payroll code for each site as his primary key. After all, each site had its own unique payroll code. Why not just repurpose that code for the database key as well?
Several months later, one of the sites decided to open a satellite office. Since it was a different physical location, it needed its own record in the Sites table-- but since it was a satellite office, they used the same payroll code to pay their staff as the parent office. When "Bryan" tried to add the new office to his database table, he received an error message because his primary key wasn't unique. But, he couldn't just modify the entry by appending a suffix either, because that would mess up queries and reports in his application.
The moral of the story is:
Make sure your primary key(s) are not entirely dependent on a single piece of data with which other human beings can screw. If you absolutely must use something like payroll code in the example above, make it a compound primary key that combines the payroll code field with a unique, independent field.
Saturday, July 4, 2009
Primary Keys Must Be Independent
Tuesday, June 23, 2009
Working with ISO 639-1 language codes in Coldfusion
Recently, I've been working on a project that uses the ISO 639-1 language codes (example: en = English, es = Spanish, etc.) to filter the results of a search. Everything works fine, until someone accidentally provides an invalid language code and no results are returned.
It's a subtle bitch of an error, when you think about it. Someone believes they are performing a search for any item written in Spanish that mentions the movie title, Fight Club, but they've accidentally provided the non-existent language code of "ed." No results are found because there isn't even an "ed" language, and our user walks away with the mistaken impression that hispanics have nothing of interest to say about Fight Club.
(I know this example seems silly, but there's a convention of using movie titles as examples in Coldfusion training. Try replacing "Fight Club" with "Election" and "hispanic" with "iranians." Doesn't seem nearly as silly now, does it?)
Clearly, if someone provides an invalid language code for a search, we want to notify them there is a problem. Then they can correct the language code and perform the search again. This problem actually consists of two parts: determining if the provided language code is valid, and then how to best notify the user if it is not.
Since the first part of the problem is more interesting to me, and far better coders have written fantastic articles and many blog entries on error handling in Coldfusion, I'm going to focus on the challenge of how to best determine if a provided language code is valid. (For the "UI expert" who reads this and says it's a non-issue because I should just display the language codes in a select/option list-- what if I don't have exclusive control over the interface because it's a web service or component with remotely accessible methods and third party developers are creating their own clients for it?)
Here's my first stab at code that compares ARGUMENTS.lang (i.e. the language code provided by the user) against the official letter codes:
<!--- first we need a list of the various ISO 639-1 language letter codes --->Sure, it gets the job done. But I think I can do better.
<cfset VARIABLES.ListISOCodes = "aa,ab,ae,af,ak,am,an,ar,as,av,ay,az,ba,be,bg,bh,bi,bm,bn,bo,br,bs,ca,ce,ch,co,cr,
cs,cu,cv,cy,da,de,dv,dz,ee,el,en,eo,es,et,eu,fa,ff,fi,fj,fo,fr,fy,ga,gd,gl,gn,gu,gv,ha,
he,hi,ho,hr,ht,hu,hy,hz,ia,id,ie,ig,ii,ik,io,is,it,iu,ja,jv,ka,kg,ki,kj,kk,kl,km,kn,ko,kr,
ks,ku,kv,kw,ky,la,lb,lg,li,ln,lo,lt,lu,lv,mg,mh,mi,mk,ml,mn,mr,ms,mt,my,na,nb,nd,
ne,ng,nl,nn,no,nr,nv,ny,oc,oj,om,or,os,pa,pi,pl,ps,pt,qu,rm,rn,ro,ru,rw,sa,sc,sd,se,
sg,si,sk,sl,sm,sn,so,sq,sr,ss,st,su,sv,sw,ta,te,tg,th,ti,tk,tl,tn,to,tr,ts,tt,tw,ty,ug,uk,
ur,uz,ve,vi,vo,wa,wo,xh,yi,yo,za,zh,zu">
<!--- checking to see if the optional ISO language code parameter was passed --->
<cfif isDefined("ARGUMENTS.lang")>
<!--- if so, we use ListFind to search for provided lang code in our list --->
<!--- if ListFind returns 0 then we know it didn't find the specified code --->
<cfif (listFind(VARIABLES.ListISOCodes,ARGUMENTS.lang) IS 0)>
<!--- this is where we throw an error, or set our returnvariable to some warning flag/message --->
</cfif>
</cfif>
Tuesday, April 14, 2009
Five things you should [but probably will not] do after installing Tomato firmware
You've installed Tomato firmware on your Linux-based router/AP and tried out all the "sexy" features. Maybe you've been obsessing over your bandwidth statistics? Or used the SSH daemon to surf the web with encryption at public WiFi spots? Or perhaps found the best channel to use for your wireless network with the wireless survey tool? Chances are, you've got your Tomato configuration features customized to maximize your situation.
And I'm sure you remembered to back your router configuration up too, right?
Yeah, me neither. ;) Don't worry-- I'm not writing this entry to "look down my nose" at people. It's easy to get carried away and overlook the mundane basics when a free download adds so much utility to your residential networking gear. Now that my initial infatuation period has passed, I'm hoping to create a basic checklist of configuration tasks to help me stay more focused and disciplined during future Tomato installation/configuration opportunities. Hopefully someone else will benefit from my oversights.
1. STAY CURRENT WITH NEW FIRMWARE: Many people download and install the Tomato firmware on their router, only to forget about it after a few weeks of experimentation and customizing. It's human nature-- if something works well, we take it for granted and focus our attention elsewhere. A few months pass, and suddenly we're missing out on great new features that would make our network situations even better. Or, in a worst case scenario, we continue using an older version of the firmware that turns out to have a security exploit in it.
You don't have to check Polarcloud's website religiously every day to see if a new firmware version is available. You can sign up for their email alert service (for the "traditional" crowd) or their RSS feed (for the "cool kids" and their aggregators).
2. TELL TOMATO WHERE TO KEEP YOUR BANDWIDTH MONITORING DATA: I don't personally understand the appeal, but people like Tomato's bandwidth monitoring feature. I guess if you have an Internet Service Provider that charges for bandwidth used instead of a flat fee that it could help settle a dispute and "keep folks honest." Unfortunately, Tomato keeps your bandwidth history in temporary memory by default. This means your historical bandwidth data disappears if your router reboots for any reason (brief power outage, configuration change that required a restart, etc.)
If you need to hang on to that data, you need to tell Tomato to keep it in a less volatile place (Administration -> Bandwidth Monitoring). I personally have Tomato saving my data into NVRAM on the router itself once per week, but then I don't have an serious need for that information. If you do, you might consider using CIFS to copy the information to a computer on your network instead and saving more often.
3. USE OPENDNS: This tip isn't Tomato specific, per se, but I think it's worth mentioning anyway. You should, at a minimum, seriously consider changing your router's DNS server settings to those provided by Open DNS. Although many people talk about the improved speed they've seen since making this change, my reason for recommending them is more security-based. Thanks to a collaborative relationship with their sister-site, Phishtank, people who use Open DNS are automatically protected from blacklisted phishing sites. By using Open DNS servers in your router's settings, any computer or device that accesses the Internet via your network enjoys that same protection.
If you like that nifty little trick, signing up for a free account with Open DNS gives you even more features and control. You will want to set up Open DNS as one of your two Dynamic DNS options in Tomato (Basic -> DDNS) to keep the service informed of any IP address changes.
4. BACKUP YOUR ROUTER CONFIGURATION: You've invested time and energy learning Linux esoterics to customize your configuration precisely how you want it. The sense of accomplishment you're feeling now won't be there when you attempt recreating that configuration from scratch because "something happened and you didn't make a backup."
Save yourself the frustration by making a backup copy of your masterpiece (Administration -> Configuration) before "something happens."
5. NOW IT'S YOUR TURN! I am sure there are more than just five "essential" configuration tasks to the Tomato Firmware. Share your "sadder, but wiser" configuration story as a comment, so everyone can learn from it.
