EMI Removes Digital Rights Management (DRM)

Music industry heavy weight EMI has announced that it will allow the download of their music catalog online without Digital Rights Management attached to the files.

DRM was released with the intention it would curb digital media piracy. Unfortunately, implementing DRM in a manner that worked well for the consumer seemed to be a constant thorn in the side of the media publishers as the consumers pushed back against the use of digital rights management.

Users would download music encrypted using DRM technology and would then not be able to transfer the song from one media device to another, such as from a computer to an MP3 player. If you could move or transfer the file, there was a limited number of transfers available. This problem was circumvented if you were downloading from the Apple iTunes store, as it would allow syncing of the downloaded songs onto associated Apple devices.

As expected, Apple will be the first retailer to sell the newly released DRM free media through the iTunes store at a slightly higher price than a DRM encrypted media file. EMI have stated that they will continue to use DRM technology where appropriate for time or subscription based services. The use of DRM in that area is well within the bounds of its reach in my opinion and is an excellent use of the technology.

From my point of view, this is excellent news. I have refused to download music from iTunes store because the files have DRM attached. Sometime in the near future, I might just swing by the iTunes store and see what they have on offer.

Oracle Dynamic SQL Using The DECODE Function

When developing most applications, you end up having to interact with a database. If you’re application is large enough and warrants the design and effort, it will no doubt be tiered as well. If your application is tiered, one of your layers will probably include a database API. A database API is used to restrict access to the database, a funnel which all requests from your application must pass through. Implementing this additional layer of abstraction normally yields in higher performance, increased security and easier maintenance.

In most cases, you’re application code will require a fairly standard interface into your database to retrieve information. A simple way to achieve that consistent interface is using the DECODE function in your PL/SQL procedures and functions. There are always cases where you need to do something special, and in those cases it is probably a perfect scenario for this methods counter-part – the string building technique.

For a simple but practical example, consider the following table definition:

  1. CREATE TABLE MyTable (
  2. PrimaryKeyID NUMBER NOT NULL,
  3. ForeignKeyID NUMBER NOT NULL,
  4. Name VARCHAR2 (255) NOT NULL
  5. )

As I have mentioned before, the Oracle DECODE function provides similar functionality to an IF-THEN-ELSE code block. Lets examine an example which relates to MyTable above.

  1. PROCEDURE Get
  2. (pDetails OUT grcDetails,
  3. pPrimaryKeyID IN MyTable.PrimaryKeyID%TYPE DEFAULT NULL,
  4. pForeignKeyID IN MyTable.ForeignKeyID%TYPE DEFAULT NULL,
  5. pName IN MyTable.Name%TYPE DEFAULT NULL)
  6. IS
  7. BEGIN
  8. OPEN pDetails FOR
  9. SELECT *
  10. FROM MyTable
  11. WHERE PrimaryKeyID = DECODE(pPrimaryKeyID, NULL, PrimaryKeyID, 0, PrimaryKeyID, pPrimaryKeyID)
  12. AND ForeignKeyID = DECODE(pForeignKeyID, NULL, ForeignKeyID, 0, ForeignKeyID, pForeignKeyID)
  13. AND LOWER(Name) LIKE DECODE(pName, NULL, LOWER(Name), '%'||LOWER(pName)||'%');
  14. END;

Working top down, the second line declares pDetails (the parameter name) as being an OUT parameter. As the name suggests, an OUT parameter is a way for you to pass information out of your procedure. In some aspects it would be similar to returning a value out of a typical programming function. The difference to an OUT parameter is that you can have many OUT parameters in a single procedure, where as a typical programming function can only return a single value at a time.

The next thing you’ve probably noticed is this weird thing in there, grcDetails. Just as the IN parameters below it, grcDetails is the type of the parameter. It would be akin to defining a paramter in a traditional function as accepting an integer or string type. In this case, grcDetails is declared in the package header:

  1. TYPE grcDetails IS REF CURSOR RETURN MyTable%ROWTYPE;

The important thing to notice about grcDetails is that it is a REF CURSOR and its type is the ROWTYPE of MyTable. The benefit of defining it as a ROWTYPE is that if tomorrow the definition of the table changed in some way, you don’t have to worry about changing any code to match the new table definition – it simply continues to work. This is the same reason why the INPUT parameters above have their type defined against the field that they represent. Of course, if you are passing in or out a value which isn’t based on a field – you would simply define it as a standard PL/SQL type.

Next, you have probably noticed that each IN parameter has a DEFAULT value. As with most programming languages, providing a DEFAULT value allows the programmer to optionally supply that parameter. For our example, this is a pivotal point as it simplifies the use of this method; more on that later.

Moving on to the actual SQL statement, the flexibility of the DECODE function starts to show. Since the DECODE function acts like an IF-THEN-ELSE block, you’re actually seeing an inline IF-THEN-ELSE IF-ELSE block per field. Lets take a practical example of that statement and assume we passed in pPrimaryKeyID with a value of 1, while the other two input parameter’s are not passed in (thus taking their default value of NULL). The SQL statement would be parsed and executed as follows:

  1. SELECT *
  2. FROM MyTable
  3. WHERE PrimaryKeyID = 1
  4. AND ForeignKeyID = ForeignKeyID
  5. AND LOWER(Name) LIKE LOWER(Name);

Of course, the net effect of that SQL statement is that the first WHERE condition is used, while the second and third conditions are nullified as the left and right half of each expression are equivalent. So by passing in a NULL value for the pForeignKeyID and pName parameters, they are effectively removed from the SQL statement by evaluating to themselves. Of course, you could also pass in two, three or none of the parameters to the procedure as well. In which case you would get either a restricted set from the statement or all records in the table.

As mentioned above, the DEFAULT value of each IN parameter is critical to this methods success. By providing a default value, the programmer no longer needs to call the procedure with all parameters; all, some or none are also perfectly valid combinations. Since the input parameter’s default value is NULL, the following DECODE function call is all that is required to negate the parameter in the WHERE clause:

  1. PrimaryKeyID = DECODE(pPrimaryKeyID, NULL, PrimaryKeyID, pPrimaryKeyID)

By now, you have probably noticed some extra parameters in the example GET procedure listed above. The additional values are used to exclude any other ‘not important’ values from the statement. In the example procedure, NULL and the value 0 are considered unimportant; whilst all other values are considered useful.

If you’re wondering why you’d want to exclude other values, it might be to make another section of your application simpler. Some programming languages don’t support nullable primitive types. If you use such a language and you intend to pass in all parameters to your procedure in all circumstances (this is the ‘simpler’ above); then all values will have a value of some sort. In the case of a primitive such as an integer, you might find that its default uninitialised value is zero. If that is the case and you don’t require the ability to filter on a zero value, then excluding it within the DECODE function makes things simpler.

Don’t think you’re limited to using this method on SELECT statements, it will work a treat on DELETE too. Consider the following DEL procedure:

  1. PROCEDURE Del
  2. (pRowCount OUT NUMBER,
  3. pRoomTypeID IN RoomTypes.ROOMTYPEID%TYPE DEFAULT NULL,
  4. pBuildingID IN RoomTypes.BUILDINGID%TYPE DEFAULT NULL)
  5. IS
  6. BEGIN
  7. IF (pRoomTypeID IS NOT NULL OR pBuildingID IS NOT NULL) THEN
  8. DELETE
  9. FROM RoomTypes
  10. WHERE RoomTypeID = DECODE(pRoomTypeID, NULL, RoomTypeID, 0, RoomTypeID, pRoomTypeID)
  11. AND BuildingID = DECODE(pBuildingID, NULL, BuildingID, 0, BuildingID, pBuildingID);
  12. ELSE
  13. RAISE_APPLICATION_ERROR(-20001, 'At least one parameter must be supplied');
  14. END IF;
  15. pRowCount := SQL%ROWCOUNT;
  16. END;

There is a caveat to using this method for building dynamic SQL within Oracle, it cannot handle columns which are nullable. If you consider the use of the DEFAULT value on all of the input parameters, it will become clear. Within Orcale PL/SQL, it is not possible to use an equality (=) operator to compare a NULL value. As a simple example, take the two simple SQL statements:

  1. SELECT * FROM DUAL
  2. SELECT * FROM DUAL WHERE NULL = NULL

The first SQL statement above will return the expected row from the DUAL table, while the second statement will return no results as you cannot compare the NULL value in that manner. If a comparison against a NULL is required, it must be handled using the IS NULL clause.

Other than not being able to use this method against columns which are nullable, its a really convenient way to write dynamic SQL in Oracle. Next time you need a little flexibility and you don’t want to go down the string building path, try using the Oracle DECODE function to produce your dynamic SQL.

The Naked Sausage

This afternoon, Claire and I dropped by the supermarket to pick up some odds and sods for dinner and among our findings were naked sausages. You’re probably wondering what the hell a naked sausage is, well its a standard sausage without the skin on it.

The particular ones we found were by a Western Australian company named Chevups. Other than the fact that the sausages were skinless, which in itself is pretty cool – they were also:

  • made of Australian lean beef
  • gluten free
  • 92% fat free

They look a little different to normal but man were they tasty – I highly recommend trying some out if you come across them.

Storing Time Information In Oracle

I recently had a requirement to store a time within Oracle, not a timestamp or a date – just the time. As it turns out, storing just the time information in Oracle isn’t something you can do without a little bit of work.

Oracle provides you a few different data types for handling date and time:

  • DATE
  • TIMESTAMP
  • INTERVAL

Unfortunately, none of the supplied data types are an exact match for a time such as 10:30 AM. The good news is that both a DATE and a TIMESTAMP data types contain time information – so it is possible to do what you want with a little slight of hand.

The slight of hand I mentioned has to do with the default behaviour of Oracle when inserting only a time component of a DATE or a TIMESTAMP data type. For the rest of this discussion, lets assume you have a simple table set up as follows:

  1. CREATE TABLE Times (
  2. id INT NOT NULL,
  3. thedate DATE NOT NULL,
  4. thetimestamp TIMESTAMP NOT NULL
  5. );

With the following three rows of data:

  1. INSERT INTO Times (id, thedate, thetimestamp) VALUES (1, SYSDATE, SYSDATE);
  2. INSERT INTO Times (id, thedate, thetimestamp) VALUES (2, TO_DATE('10:30 AM', 'HH:MI AM'), TO_DATE('10:30 AM', 'HH:MI AM'));
  3. INSERT INTO Times (id, thedate, thetimestamp) VALUES (3, TO_DATE('0001-01-01 10:30 AM', 'YYYY-MM-DD HH:MI AM'), TO_DATE('0001-01-01 10:30 AM', 'YYYY-MM-DD HH:MI AM'));

When selecting that grid of information out, you’ll receive:

id thedate thetimestamp
1 28/03/2007 11:32:25 PM 28/03/2007 11:32:25.000000 PM
2 1/03/2007 10:30:00 AM 1/03/2007 10:30:00.000000 AM
3 1/01/0001 10:30:00 AM 1/01/0001 10:30:00.000000 AM

There are more than one solution to this problem, as if you’re using any of the standard date/time data types – you can always store the time component. What may or may not be of interest to everyone is what Oracle does with the date component of a DATE or TIMESTAMP data type when you don’t provide the standard date components of a date/time data type.

If you take notice of row #1 returned, you’ll see that because the inserted values for thedate and thetimetamp was SYSDATE, it has stored the date component as you’d expect.

Compare that against row #2 and you’ll notice that the insert statement simply provided the time component and made no mention of the date. When Oracle returned that time value out, it has automatically substituted the first day of the current month into the date component.

When inserting row #3, an arbitrary date of 1 Jan 0001 was supplied. When reading that information back out of Oracle, it handles the date component in a similar fashion to how it was handled for row #1 using the SYSDATE; ie you get the expected result back.

Since you can return any number of rows from Oracle doing standard date/time arithmetic, it really comes down to preference. What option above has the most semantic meaning to a user or the data in the database when you’re required to store a time and not a timestamp?

For consistency reasons, my personal preference leans towards applying technique #3 and here are a couple of simple reasons why:

  • When dealing with a DATE or TIMESTAMP value stored like that in PL/SQL, you’ll always know what value is stored in the date component of the value.
  • If you’re handling this information in an application language such as Microsoft .NET, Java or Python – you will always know what value to expect in the date component. Using technique #1, the date component will change for every row. Using #2, the date component of the value will change per row depending on what month the row was inserted

Does anyone else have a hot tip for storing a straight time value in Oracle?

Search Engine Optimisation & Referral Tracking

If you’re looking to set up an affiliate network or you’ve already got one, you should be aware of a couple important points which might just change how you have or are thinking about setting it up.

Physically setting up an affiliate program is quite straight forward and you have two choices in handling the inbound links and referral tracking:

Single entry point
Using a single entry point in your site, where everyone links to with their respective referral code which then shunts the user to the desired page. Using this method, you might end up with:

  • http://mydomain.com/tracking.php?ref=abc&destination=1
  • http://mydomain.com/tracking.php?ref=abc&destination=2
  • http://mydomain.com/tracking.php?ref=abc&destination=3
Multiple entry points
Allowing multiple entry points facilitates deep linking. If you allow multiple entry points, you might end up with:

  • http://mydomain.com/page-1/?ref=abc
  • http://mydomain.com/page-2/?ref=abc
  • http://mydomain.com/page-3/?ref=abc

Both of these methods will work but which one is better for search engine optimisation? If you use a single entry point, you end up in a position where you’ll have hundreds or thousands of inbound links to a particular page. Unfortunately, the page that they are linking to isn’t useful to a search engine for indexing – it simply redirects to another page. You do however get the benefit of being able to effortlessly reorganise a web sites structure and only have to worry about updating destination URL’s in a single location.

Using multiple entry points allows your marketing or affiliates to link directly to their intended page with their referral code, which can make a difference on various levels:

  • it’s convenient for the people linking to the page
  • it’s less error prone, as the linker can simply copy the URL from the browser
  • the linked URL will begin to gain inbound links, which is critical for effective search engine optimisation
  • the person clicking on the URL can hover the URL and see where it is going

The last point might seem like something you might otherwise gloss over, however as internet users become more savvy – they are becoming acutely aware of their online actions. Letting the user clicking on the link see the destination URL will help build trust between your web site and them, as they will be less inclined to think the link is spam.

My personal preference is towards deep linking, it’s just so convenient. If you allow deep linking, the next problem you have is your affiliated links making their way into the search engine result pages; which is definitely not what you want. Fortunately, through the use of a robots.txt file it is possible to drop the affiliated URL’s from being indexed. In the above multiple entry point example, you could stop those URL’s from being indexed by including the following line into your robots.txt file:

  1. User-agent: *
  2. Disallow: /?ref

Unfortunately, your work isn’t quite done, as all of the inbound links are linking into distinct URL’s (ie, with the referral code). As far as a search engine is concerned, these are totally separate web pages which could/should have unique content. To leverage the most out of your inbound links, you want to make sure the link ends up pointing to the permanent URL for the content (ie, without the referral code).

Remembering that you are tracking referral codes, the web site must first do something useful with the referral code. Useful might be placing the referral code in a cookie for later use or storing it in a database, but something generally needs to happen with it. Once the useful action has been completed, you need to send a standard HTTP redirect to the user agent (browser, bot, ..) to tell it the permanent URL for that content exists at a different URL – in this case the same URL without the referral code. Consult the documentation for your favourite server side language about handling HTTP response codes.

By implementing these two simple techniques, you now only have a single copy of any of your web pages indexed in the search engines and any inbound referral links will ultimately be attributed to the permanent URL for the actual content.

You can now sleep easily at night knowing you have search engine optimised referral tracking.