TYPO3 has been designed to use MySQL from the beginning. With TYPO3 3.6.0 a database wrapper class has been introduced in all of the core and default global extensions. This means that implementation of various Database Abstraction Layers (DBAL) is now possible. But by default TYPO3 works only with MySQL - and still does this with priority.
In general you can therefore say that TYPO3 is optimized for MySQL while supporting other databases through DBALs implemented as extensions.
The implementation of DBAL was originally implemented in its first version by Kasper Skårhøj, but development is currently headed by Karsten Dambekalns.
The wrapper class is called “t3lib_DB” and is instantiated globally as $TYPO3_DB.
The class contains three sections of functions
MySQL wrapper functions: You can substitute you hardcoded MySQL calls directly with these without further modifications. This is step-1 towards database abstraction support in your extensions!
Query building functions: Instead of constructing SELECT, UPDATE, INSERT and DELETE statements directly you can use API functions in the wrapper class for this. This requires a bit more re-design from you but you will get better security in your scripts (prevents SQL injection for UPDATE/INSERT at least) and is step-2 towards database abstraction support in your extensions!
Query execution functions:There are functions that combines the creation of SELECT, UPDATE, INSERT and DELETE statements with immediate execution of them. Using these functions is the final step to DBAL support since you allow the wrapper class (and any DBAL implementation on top of it) to create the query and execute it according to the underlying storage method.
The coding guidelines require that you use the new database wrapper class. The minimum is that you use the mysql-wrapper functions instead of hardcoded calls.
This is easy though. Just look at these examples:
$res = mysql(TYPO3_db, 'SELECT * FROM mytable WHERE uid=123 AND title LIKE "%blabla%" ORDER BY title LIMIT 5');
while($row = mysql_fetch_assoc($res)) {
echo $row['title'].'<br />';
}
$res = mysql(TYPO3_db, 'INSERT INTO mytable (pid,title) VALUES (123, "My Title")');
$res = mysql(TYPO3_db, 'UPDATE mytable SET title="My Title" WHERE uid=123');
$res = mysql(TYPO3_db, 'DELETE FROM mytable WHERE uid=123');
This would be re-written to:
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, 'SELECT * FROM mytable WHERE uid=123 AND title LIKE "%blabla%" ORDER BY title LIMIT 5');
while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
echo $row['title'].'<br />';
}
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, 'INSERT INTO mytable (pid,title) VALUES (123, "My Title")');
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, 'UPDATE mytable SET title="My Title" WHERE uid=123');
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, 'DELETE FROM mytable WHERE uid=123');
As you can see “mysql_fetch_assoc()” has “my...” stripped and the global database object prepended and thus becomes “$GLOBALS['TYPO3_DB']->sql_fetch_assoc()”. This is the same for all other supported MySQL calls.
Notice: It is not every single MySQL call that is supported. Only those found used in the core of TYPO3 at the time of the implementation of t3lib_DB was included. Please see “class.t3lib_DB.php” for the complete list of support.
You can also use the wrapper class to build your queries. The greatest advantage of this is that you avoid SQL injection possibilities in all INSERT and UPDATE queries and generally get a more clean code. The idea is that you call a function with some parameters and in return you will get a SQL query you can pass to $GLOBALS['TYPO3_DB']->sql()
Look at the SQL examples above; this is how the same set of code would look using the query building functions in the wrapper class:
// SELECT:
$query = $GLOBALS['TYPO3_DB']->SELECTquery(
'*', // SELECT ...
'mytable', // FROM ...
'uid=123 AND title LIKE "%blabla%"', // WHERE...
'', // GROUP BY...
'title', // ORDER BY...
'5' // LIMIT ...
);
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, $query);
// INSERT:
$insertArray = array(
'pid' => 123,
'title' => "My Title"
);
$query = $GLOBALS['TYPO3_DB']->INSERTquery('mytable', $insertArray);
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, $query);
// UPDATE:
$updateArray = array(
'title' => "My Title"
);
$query = $GLOBALS['TYPO3_DB']->UPDATEquery('mytable', 'uid=123', $updateArray);
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, $query);
// DELETE
$query = $GLOBALS['TYPO3_DB']->DELETEquery('mytable', 'uid=123');
$res = $GLOBALS['TYPO3_DB']->sql(TYPO3_db, $query);
As you can see the methods SELECTquery, UPDATEquery, INSERTquery and DELETEquery are called to create the actual SQL queries.
It might seem as overhead at first glance but in particular the INSERT and UPDATE queries will help make you code much better since you fill in an associated array which you simply pass to a function and say “Please, give me the SQL needed to insert / update this record in the database”.
Looking at the select statement that is broken into 6 pieces/arguments. The function is very easy to use if you are used to putting together a SELECT query:
Creating this select query is easily done...
SELECT [arg1] FROM [arg2] WHERE [arg3] GROUP BY [arg4] ORDER BY [arg5] LIMIT [arg6]
... by calling
$GLOBALS['TYPO3_DB']->SELECTquery('[arg1]', '[arg2]', '[arg3]', '[arg4]', '[arg5]', '[arg6]');
The advantage is that we can control more tightly each part of the SELECT query if needed. In particular the “LIMIT” argument differs among databases and therefore important to separate. But it might also come in handy to have the other parts separated.
The best possibly solution which will provide 100% database abstraction support is if you use the functions in the wrapper class prepended with “exec_” - they all do the same as the query building functions but they execute the query immediately after thus returning a result pointer which can be traverse by ->sql_fetch_assoc() as usual.
Example (compare to listing above):
// SELECT:
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
'*', // SELECT ...
'mytable', // FROM ...
'uid=123 AND title LIKE "%blabla%"', // WHERE...
'', // GROUP BY...
'title', // ORDER BY...
'5,10' // LIMIT to 10 rows, starting with number 5 (MySQL compat.)
);
// INSERT:
$insertArray = array(
'pid' => 123,
'title' => "My Title"
);
$res = $GLOBALS['TYPO3_DB']->exec_INSERTquery('mytable', $insertArray);
// UPDATE:
$updateArray = array(
'title' => "My Title"
);
$res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery('mytable', 'uid=123', $updateArray);
// DELETE
$res = $GLOBALS['TYPO3_DB']->exec_DELETEquery('mytable', 'uid=123');
Normally when you execute a MySQL query with eg. “mysql_query” or the like you will get a result resource pointer back. But DBAL implementations will more likely return an object to you! In most cases you can treat it the same in you programming; simply take the “$res” from the query and pass to the function traversing the resutl (like $GLOBALS['TYPO3_DB']->mysql_fetch_assoc()) and it will be the same.
However, there is one occasion where you should beware; if you pass the result variable to functions make sure you pass it by reference! If you don't copies of the object will be made (under PHP4 which still must be supported!). This is not only slower in PHP-execution but it also means that internal result pointers might be reset in the process or otherwise lost.
So; program your application as if the result from the query executions are objects; pass them around by reference.
Please follow these guidelines to make your SQL fully DBAL compliant (portable to database such as Oracle and PostGreSQL etc).
Escaping quotes: Remember to pass all values in WHERE clauses through $GLOBALS['TYPO3_DB']->fullQuoteStr() and add the table name as second parameter so DBAL layers can escape them correctly according to specific handlers if any.
Quote strings with '' (single quote), not "" (double quote) if you use ->quoteStr() instead of ->fullQuoteStr() (which you should always do if the string is unknown!)This is needed because all databases aside from MySQL do only accept single quotes for marking literal strings. Double quotes (that can be used in MySQL for this) are used for field names in all other databases, so any other database than MySQL will look for non-existant fields if you use double quotes.The reccomendadtion clearly is to use fullQuoteStr() whereever possible!
If you use the marker "###PAGE_TSCONFIG_STR###" anywhere, you must put it in single quotes!
Joining tables: Always prefix fieldnames in the query with the corresponding tablename. If you don't the field mapping of DBAL extension may fail.
Boolean evaluation?:
No boolean evaluation of integers: Do not evaluate an integer field as a boolean, eg. "AND hidden". Rather "AND hidden=1" (or "AND hidden!=0" or "AND NOT(hidden=0)"). This is needed for databases that refuse to do boolean operations on numeric fields (like PostgreSQL).
WHERE 1=1: If a where clause is needed but only to be true, you might be using a "1" (like "WHERE 1" or "WHERE ... AND 1") but this should be a comparison like "1=1" instead (like "WHERE 1=1" or "WHERE ... AND 1=1"). This is again needed because of some databases not doing boolean operations on integer types.
Negation operator: Do not negate values with "!" like "!personal", rather use "personal=0". The ! isn't a standard SQL operator, as such it doesn't work on all databases.
Aliases:
No AS keyword: Do not use the "AS" keyword in SQL aliases for *tables* (not fields, that is OK it seems) like "pages AS A", rather "pages A" (exclude "AS")
Alias for COUNT(*): Use eg. "COUNT(*) AS icount" in you want to access "COUNT(*)" as an associative key in the result array. This is needed because there is not common way to name such columns in the result set, therefore providing a name is needed.
Field definitions:
Never use UNSIGNED attributes for 32 bit integer fields.
In general, using tinyint, smallint etc with/without the UNSIGNED attribute is a MySQL specific definition which will work with DBAL but is depricated. 32 bit signed integers is the common denominator in DBAL.
In TYPO3 we use a subset of MySQL compliant SQL as our “abstraction language”. It means that the SQL is readily executed by MySQL while it may need translation if any other database is needed.
To make sure your SQL is compliant with the TYPO3 SQL subset you can use the class t3lib_sqlparser to parse it.
Generally, the SQL you can use in TYPO3 should be rather “simple”. Luckily TYPO3s core itself is rather conservative in the SQL usage. It turns out that 95% of all select queries are very simply, just looking up a list of fields from a certain table, having a where clause using AND, OR, =, <, >, NOT, LIKE, IN and possibly ordering the result by one or two fields and possibly applying a LIMIT in the end. This is relatively easy to translate to other databases!
For the last 5% we are using simple joins, aliases, LEFT JOIN, SUM(), AVG, COUNT() functions and GROUP BY. These might break compatibility but I assess they are still in the range of what can be translated or directly understood by other SQL databases.