Making an application portable among database engines is not impossible...
but it's very hard.
There are exactly two techniques you can bring to the problem, and you can use
both.
LCD
The first technique is to use only the "least common denominator" features
among the SQL engines to which you wish your application to be portable. What
you wind up doing here is to build,
ad hoc, in languages not designed
for it (Perl, Ruby, PHP, etc.), those features of the RDBMS engine which you
need and aren't in the "least common denominator." Performance suffers, and
high bug rates are normal because you'll be using tools not designed to create
a database engine. It's possible to pound nails with a screwdriver, but it's
not easy and it's not fun.
Feudin' Backends
The second technique involves building separate database interface code for
each RDBMS engine. Constructing and maintaining the code for each RDBMS is
not by itself a difficult task, but keeping them all in sync and functionally
equivalent is. The difficulty increases like O(n^2) for n RDBMSs.
In a later essay: Cases where RDBMS portability is essential.
each RDBMS engine."
This method tends to make you very aware of which RDBMSs are helping you, and which leave all the work for you to do.
"The difficulty increases like O(n^2) for n RDBMSs."
Interesting, I wouldn't expect that.
Actually, n^2 is somewhat optimistic, as it's assuming that RDBMS back-ends behave in some kind of constant way, and some don't, so you're adding in complexity at each point where they change behavior, which may in effect be hundreds of "different" RDBMSs instead of some number bounded from above by 10.
I'm not a fan of these myself, but it's worth mentioning as a possibility, even if that's only to warn against them.