Flourish SQL

One of the aims of Flourish is to make a portable PHP library so that applications can be deployed on different servers with little to no changes. With the multitude of database servers available and different platforms, one of the main hurdles for portability is the different dialects of SQL.

Flourish SQL has grown a common subset of the SQL dialects supported by Microsoft SQL Server, MySQL, PostgreSQL and SQLite, with a few additions for ease-of-use. This document describes what features and syntax are supported across all databases.

In the Flourish SQL column you will find the supported syntax for a function, data type, etc. The other columns contain what the syntax will be run as on the specified database. Inconsistencies between Flourish SQL and the specific databases are highlighted in red.

Supported Database Versions

The information on this page is targeted at the following database versions, or newer editions:

  • MSSQL: Server 2005
  • MySQL: 5.0
  • Oracle: 10g
  • PostgreSQL: 8.0
  • SQLite: 2.8.17 (v2), 3.2.8 (v3)

Usage

The following Flourish SQL is supported when calling the fDatabase::translatedQuery() and fDatabase::unbufferedTranslatedQuery() methods. The Cross-Database SQL section of the fDatabase page has more information.

Data Types

Most dialects of SQL support a large number of common data types, however a things like booleans, binary data and dates and times are often different. Here is an outline of the data types supported by Flourish SQL:

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
smallint smallint smallint smallint smallint smallint
integer integer integer integer integer integer
bigint bigint bigint integer bigint bigint
integer autoincrement primary key integer identity(1) primary key integer auto_increment primary key integer + sequence + trigger serial primary key integer primary key autoincrement
float float float float float float
real real real real real real
decimal decimal decimal decimal decimal decimal
char nchar char char char char
varchar nvarchar varchar varchar2 varchar varchar
text ntext mediumtext clob text text
blob image longblob blob bytea blob
timestamp datetime datetime timestamp timestamp timestamp
date date for 2008, datetime for 2005 date date date date
time time for 2008, datetime for 2005 time timestamp time time
boolean bit boolean number(1) boolean boolean

In all cases except for the date and time are the data types consistent across the databases. MSSQL does not support date or time, so the more precise datetime is used instead. Oracle does not support time and uses timestamp instead. In both cases, the unnecessary portions are removed by the unescape() method of fDatabase.

One other thing to note is the apparent perfect compatibility for SQLite data types. This is not because the SQLite data types were chosen as the basis for Flourish SQL, but rather that SQLite is loosely-typed and allows anything to be entered for a data type.

Operators

Operators are fairly consistent across the different databases, with the exception of the concatenation operator. Please note that || works for concatenation in MySQL only when MySQL is in ANSI mode (Flourish automatically switches into ANSI mode when a connection to a MySQL database is initiated).

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
Mathematical Operators
+ + + + + +
- - - - - -
/ / / / / /
* * * * * *
% % % mod() % %
String Operators
|| (concatenation) + || || || ||
Comparison Operators
< < < < < <
> > > > > >
<= <= <= <= <= <=
>= >= >= >= >= >=
<> (inequality) <> <> <> <> <>
!= != != != != !=
= = = = = =
IN IN IN IN IN IN
NOT IN NOT IN NOT IN NOT IN NOT IN NOT IN
IS (equality with NULL) IS IS IS IS IS
IS NOT (inequality with NULL) IS NOT IS NOT IS NOT IS NOT IS NOT
LIKE (case insensitive) LIKE LIKE lower(value) LIKE lower(pattern) ILIKE LIKE
Boolean Operators
AND AND AND AND AND AND
OR OR OR OR OR OR

Functions

There a quite a few functions that are consistent across the different databases:

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
Mathematical Functions
abs(x) abs(x) abs(x) abs(x) abs(x) abs(x)
acos(x) acos(x) acos(x) acos(x) acos(x) PHP Callback
asin(x) asin(x) asin(x) asin(x) asin(x) PHP Callback
atan(x) atan(x) atan(x) atan(x) atan(x) PHP Callback
atan2(x, y) atn2(x, y) atan2(x, y) atan2(x, y) atan2(x, y) PHP Callback
ceil(x) ceiling(x) ceil(x) ceil(x) ceil(x) PHP Callback
ceiling(x) ceiling(x) ceiling(x) ceil(x) ceiling(x) PHP Callback
cos(x) cos(x) cos(x) cos(x) cos(x) PHP Callback
cot(x) cot(x) cot(x) (1/tan(x)) cot(x) PHP Callback
degrees(x) degrees(x) degrees(x) (x * 57.295779513083) degrees(x) PHP Callback
exp(x) exp(x) exp(x) exp(x) exp(x) PHP Callback
floor(x) floor(x) floor(x) floor(x) floor(x) PHP Callback
ln(x) log(x) ln(x) ln(x) ln(x) PHP Callback
log(b, x) (log(x)/log(b)) log(b, x) log(b, x) log(b, x) PHP Callback
pi() pi() (pi()+0.0000000000000) 3.14159265358979 pi() PHP Callback
power(x, y) power(x, y) power(x, y) power(x, y) power(x, y) PHP Callback
radians(x) radians(x) radians(x) (x * 0.017453292519943) radians(x) PHP Callback
random() rand() rand() (abs( dbms_random.random ) / 2147483647) random() (abs( random()) / 9223372036854775807)
round(x) round(x) round(x) round(x) round(x) round(x)
sign(x) sign(x) sign(x) sign(x) sign(x) PHP Callback
sqrt(x) sqrt(x) sqrt(x) sqrt(x) sqrt(x) PHP Callback
sin(x) sin(x) sin(x) sin(x) sin(x) PHP Callback
tan(x) tan(x) tan(x) tan(x) tan(x) PHP Callback
Aggregate Functions
avg(l) avg(l) avg(l) avg(l) avg(l) avg(l)
count(l) count(l) count(l) count(l) count(l) count(l)
max(l) max(l) max(l) max(l) max(l) max(l)
min(l) min(l) min(l) min(l) min(l) min(l)
sum(l) sum(l) sum(l) sum(l) sum(l) sum(l)
String Functions
trim(s) rtrim(ltrim(s)) trim(s) trim(s) trim(s) trim(s)
rtrim(s) rtrim(s) rtrim(s) rtrim(s) rtrim(s) rtrim(s)
ltrim(s) ltrim(s) ltrim(s) ltrim(s) ltrim(s) ltrim(s)
upper(s) upper(s) upper(s) upper(s) upper(s) upper(s)
lower(s) lower(s) lower(s) lower(s) lower(s) lower(s)
substr(s, start, length) substring(s, start, length) substr(s, start, length) substr(s, start, length) substr(s, start, length) substr(s, start, length)
replace(s, find, replace) replace(s, find, replace) replace(s, find, replace) replace(s, find, replace) replace(s, find, replace) replace(s, find, replace)
length(s) len(s) length(s) length(s) length(s) length(s)
coalesce(a, b,...) coalesce(a, b,...) coalesce(a, b,...) coalesce(a, b,...) coalesce(a, b,...) coalesce(a, b,...)
Date/Time Functions
CURRENT_TIMESTAMP CURRENT_TIMESTAMP CURRENT_TIMESTAMP CURRENT_TIMESTAMP CURRENT_TIMESTAMP datetime( CURRENT_TIMESTAMP, 'localtime')

A quick scan of the SQLite column indicates that the trigonometric functions are all implemented as PHP callbacks. SQLite made the decision to include only specific functions in the supported SQL dialect, however they added support for hooks into other programming languages. Flourish uses this ability to provide support for trigonometric functions.

Boolean Values

Since boolean fields are implemented quite differently across databases, it comes as no surprise that there are different values to use for boolean fields:

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
TRUE '1' TRUE 1 TRUE '1'
FALSE '0' FALSE 0 FALSE '0'

Expressions

There are a few expressions that are consistent across databases:

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
BETWEEN BETWEEN BETWEEN BETWEEN BETWEEN BETWEEN
CASE (simple) CASE CASE CASE CASE CASE
CASE (complex) CASE CASE CASE CASE CASE

Joins

There are quite a few different types of join supported by the four different databases, however the common join functionality is a small subset. Note that all joins except CROSS and , require use of an ON clause, while CROSS and , can not use an ON clause.

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
, (cross join) , , , , ,
CROSS JOIN CROSS JOIN CROSS JOIN CROSS JOIN CROSS JOIN CROSS JOIN
[INNER] JOIN [INNER] JOIN [INNER] JOIN [INNER] JOIN [INNER] JOIN [INNER] JOIN
LEFT [OUTER] JOIN LEFT [OUTER] JOIN LEFT [OUTER] JOIN LEFT [OUTER] JOIN LEFT [OUTER] JOIN LEFT [OUTER] JOIN

The brackets indicate the words OUTER and INNER are optional.

Other Join Types

You may notice the lack of a FULL [OUTER] JOIN, RIGHT [OUTER] JOIN, the keyword NATURAL and USING clauses. Unfortunately MySQL and SQLite do not support FULL joins, SQLite does not support RIGHT joins and MSSQL does not support NATURAL joins. USING (col, ...) clauses are not supported by MSSQL.

FULL joins can be achieved by a somewhat complex combination of a SELECT from the first table with extra columns UNIONed with an INNER JOIN, UNIONed with a SELECT from the second table with extra columns.

RIGHT joins can be performed by switching the order of the tables in the FROM clause.

NATURAL joins are just shorthand for the common columns between tables, so all that needs to be done is an INNER join with manually specified columns in the ON condition.

USING clauses can be approximated with a ON condition, however the USING operator ensures that only one copy of each column is included in the returned rows, whereas ON does not.

Clauses

There are a few clauses that are supported across all databases:

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
DISTINCT (follows SELECT) DISTINCT DISTINCT DISTINCT DISTINCT DISTINCT
ALL (follows SELECT) ALL ALL ALL ALL ALL
LIMIT (without an offset) TOP LIMIT subquery with rownum clause LIMIT LIMIT
LIMIT/OFFSET subquery with row_number() clause and removal of extra column via PHP LIMIT/OFFSET nested subqueries with rownum clauses and removal of extra column via PHP LIMIT/OFFSET LIMIT/OFFSET
UNION UNION UNION UNION UNION UNION
UNION ALL UNION ALL UNION ALL UNION ALL UNION ALL UNION ALL

Transactions

Transaction control differs slightly among the supported databases:

Flourish SQL MSSQL MySQL Oracle PostgreSQL SQLite
BEGIN BEGIN TRANSACTION BEGIN auto-commit disabled BEGIN BEGIN
COMMIT COMMIT COMMIT COMMIT COMMIT COMMIT
ROLLBACK ROLLBACK ROLLBACK ROLLBACK ROLLBACK ROLLBACK

Foreign Key Support

All of the databases supported by Flourish support foreign key constraints through some method. By using translatedQuery() to execute CREATE TABLE statements, you can be sure that foreign keys will be enforced.

Both PostgreSQL and MSSQL support foreign keys completely natively. MySQL supports them natively as long as the innodb engine type is specified in the CREATE TABLE statement.

SQLite supports the syntax, however enforcement has to be done through triggers. translatedQuery() will create the necessary triggers to enforce foreign key constraints on an SQLite database.

Create Table/Index Syntax

Since each database supported has quite a few proprietary extensions to the SQL standard, below is a grammar that can be used with Flourish to ensure that CREATE TABLE and CREATE INDEX statements will work on all databases. Element in [] are optional, ... indicates that an element can be repeated, and | inside of {} indicates that any of the pipe-separated elements can be chosen.

Create Table

CREATE TABLE table_name ( 
    {
        column_name data_type [ DEFAULT default_value ] [ column_constraint [ ... ] ] |
        table_constraint
    }, ...
)

where table_constraint is:

{
    UNIQUE ( column_name [, ... ] ) |
    PRIMARY KEY ( column_name [, ... ] ) |
    CHECK ( expression ) |
    FOREIGN KEY ( column_name ) REFERENCES referenced_table ( referenced_column )
        [ ON DELETE action ] [ ON UPDATE action ]
}

and column_constraint is:

{
    NOT NULL | 
    NULL | 
    UNIQUE |
    PRIMARY KEY |
    CHECK ( expression ) |
    REFERENCES referenced_table ( referenced_column ) [ ON DELETE action ] [ ON UPDATE action ]
}

and action is:

{ RESTRICT | CASCADE | SET NULL | NO ACTION }

Create Index

CREATE [ UNIQUE ] INDEX index_name ON table_name ( column_name [, ... ] )