Skip to the content
Documentation
Help Start
Reading in:
  • German: 9,059 of 9,059 pages
About the help in other languages
All help pages

Writing portable SQL

How to write queries that run on every database Exponential supports - escaping and casting, limits as parameters, the dialect differences of each driver (row limits, ids, concatenation, MD5, bitwise operators, joined…

Download as PDF

Address: https://doc.exponential.earth/learninminutes/exphelp/viewer/help/software-engineers/writing-portable-sql.md

On this page

Writing portable SQL

This page is for engineers who write SQL in kernel or extension code. Exponential runs on six databases, so a query that works on your development database is not finished until it works on the others. The rules and the dialect table come from Database and SQL standards.

Contents

  1. The two mandatory rules
  2. Prefer the API to SQL
  3. Dialect differences
  4. Patterns that stay portable
  5. Testing on more than one engine

The two mandatory rules

  1. Escape every string with $db->escapeString( $value ), inside single quotes. Each driver uses the engine's own function; the Oracle driver doubles single quotes.
  2. Cast every number with (int) before it goes into SQL. A cast cannot be escaped around.

These rules are what the 6.0.13 security fixes restored in the kernel (integer casts, escapeString(), a column whitelist for ORDER BY); the same patterns in extension code are not fixed by a kernel update (Security hardening of 6.0.13).

Prefer the API to SQL

  • eZPersistentObject::fetchObjectList() and the other persistent-object methods escape for you.
  • Content queries go through fetch functions or the content classes; they apply permissions and work on every engine, MongoDB included.
  • Pass offset and limit as the parameters of arrayQuery(), so each driver writes its own syntax.

Dialect differences

TopicSQLite 3PostgreSQLMySQL, MariaDBOracleMongoDB adapter
Row limitLIMIT offset, countLIMIT count OFFSET offsetLIMIT offset, countOFFSET n ROWS FETCH NEXT m ROWS ONLYlimit and skip
New idslastInsertRowID()sequence <table>_<column>_seqAUTO_INCREMENTsequence s_<table>a counter document per sequence
String concatenation||||concat( ... )||concat( ... ), read by the adapter
MD5user function md5()needs pgcryptoMD5()STANDARD_HASH( s, 'MD5' )hashed in PHP
MySQL UPDATE ... JOINrewritten (needs SQLite 3.33)not rewrittennativenot rewrittennot applicable
Empty string''''''stored as NULLnot checked
String comparison= with casewith casewithout case (default collations)with casewith case unless a collation is set
Start of a transactionBEGIN IMMEDIATE after a writer lockBEGIN WORKBEGIN WORKno auto commitnone

The full table, with sources, is in Dialect differences, driver by driver.

Patterns that stay portable

Instead ofWrite
LIMIT 10 in the SQL stringarray( 'offset' => 0, 'limit' => 10 ) as the parameter
UPDATE a JOIN b ...UPDATE a SET ... WHERE id IN ( SELECT ... ), or two statements in a transaction
Comparing to ''handle '' and NULL alike, because Oracle stores the empty string as NULL
Relying on case-insensitive =normalize case in PHP or on both sides
SET @var := ...read the value in PHP and pass it into the next statement
An engine function (GROUP_CONCAT, ILIKE)do the work in PHP, or branch on the driver with a fallback for every engine
ORDER BY a column name from the requesta whitelist of allowed columns

Testing on more than one engine

  • SQLite needs no server: run your code against an SQLite installation as a second engine.
  • CI runs the database tests on MySQL 8.0 and PostgreSQL 15 (PHP 8.3); reproduce them with tests/runtests.php --dsn ... --db-per-test against a throwaway database (CI gates).
  • Tests that need a live database read its location from environment variables and skip when it is not set (DB-less tests and test suites).

Become an Exponential Partner

Partners share their information, provide funding, contribute back and develop the products with us, and that is how Exponential grows. A partner website and dashboard are planned for mid-2027: please become a partner today to speed up our growth rate and land new features faster.

Something wrong on this page? Report an error

Documentation license

This documentation is free documentation, licensed under the GNU Free Documentation License 1.3 or later.

Exponential Copyright © 1998-2026 Exponential Foundation and 7x