A
documentation
change is requested elsewhere. For Drupal core (and possibly other projects), once the change has been committed, this status should be recorded in a change record node.
The "double pipe" operator is standard ANSI SQL, but it is not that well supported across the board:
It doesn't seem to be supported in every platforms by Oracle.
It is not supported at all by SQL Server, which uses
+
for this. We can easily replace
||
by
+
, but that breaks automatic casting to string, when mixing the types of columns in a expression (like
'The age of ' || name || ' is ' || age || '.'
we use in
DatabaseAnsiSyntaxTestCase::testFieldConcat()
).
It is not supported at all by Drizzle, which doesn't have the PIPES_AS_CONCAT SQL Mode that MySQL supports.
For all those reasons, I suggest we strongly advice against using the double pipe operator, and standardize on
CONCAT(anyvalue, anyvalue)
(with two parameters only).
If CONCAT() is part of ANSI SQL, sounds fine to me. It should be added to the handbook page.
Perhaps we should consider adding a link from the main docblock for the DB layer to the handbook page when it's more fleshed out.
Hm. What about hshana's point in #4? I'd rather go with "ANSI and supported by the core DBs and some others" than "not-ANSI and supported by the core DB and some others", for different "others".
@Crell: CONCAT() is easily supported in all the databases. || is not supported (and *impossible to support*) in a least two databases: SQL Server and Drizzle.
@bojanz:
CONCAT()
has been the standard way of concatenating strings for years in any version of Drupal < 7. We already have a compatibility CONCAT() function in PostgreSQL and SQLite.
Just installed HEAD on PostgreSQL 8.3 and test failed:
PDOException: SQLSTATE[42883]: Undefined function: 7 ERROR: function concat(bigint, unknown) does not exist LINE 1: SELECT CONCAT($1, CONCAT(name, CONCAT($2, CONCAT(age, $3))))... ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts.: SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age; Array ( [:a1] => The age of [:a2] => is [:a3] => . [:age] => 25 ) in DrupalTestCase->run() (line 442 of /home/josh/projects/drupal7/modules/simpletest/drupal_web_test_case.php).
Here is our existing code:
if (!db_query("SELECT COUNT(*) FROM pg_proc WHERE proname = 'concat'")->fetchField()) {
db_query('CREATE OR REPLACE FUNCTION "concat"(text, text) RETURNS text AS
\'SELECT $1 || $2;\'
LANGUAGE \'sql\''
The problem is for every argument data type you can pass to CONCAT you need to specify another function. Ugly.
CREATE OR REPLACE FUNCTION "concat"(anynonarray, anynonarray) RETURNS text AS
'SELECT CAST($1 AS text) || CAST($2 AS text);'
LANGUAGE 'sql'
CREATE OR REPLACE FUNCTION "concat"(text, anynonarray) RETURNS text AS
'SELECT $1 || CAST($2 AS text);'
LANGUAGE 'sql'
CREATE OR REPLACE FUNCTION "concat"(anynonarray, text) RETURNS text AS
'SELECT CAST($1 AS text) || $1;'
LANGUAGE 'sql'
(yes, we need three functions, because all anynonarray must be of the same type when a polymorphic functions is called in PostgreSQL)
How many of the situations where CONCAT() is used can't be designed around? Looking at the bug issues involving it, I get the feeling that most can.
Most of the CAST() issues are related to the use of CONACT() too, so trying to move away from its use would reduce or eliminate two of the big areas of database compatibility problems.
I think that any documentation on this issue should suggest in strong terms you need to think hard, at least twice, before deciding that CONCAT() and therefore probably CAST() are part of the solution to your problem.
What we need is being able to replace
a || b
by something as complex as
CAST(a AS text) + CAST(b AS text)
. Could we use mapConditionOperator() for that?
Josh, do you really believe that you can parse a random SQL expression with a regexp? Remember that there might be *anything* on both sides of the operator, and that you have to take into account precedence rules.
MySQL CONCAT takes any number of parameters -- but then I guess we can document that plz do not use such CONCAT (good luck with people keeping that one...)
The concat(text, text) function is already created several lines before the place where this patch is applied, so we are creating/replacing the function twice for the (text, text) arguments.
Also, in HEAD the patch applied with an offset of 32 lines.
If CONCAT() is part of ANSI SQL, sounds fine to me. It should be added to the handbook page.
Perhaps we should consider adding a link from the main docblock for the DB layer to the handbook page when it's more fleshed out.