Problem/Motivation
In the commit https://git.drupalcode.org/project/drupal/-/commit/093e02fb731756bcc75b0c7af0228357034f2822 a new table "TEST_UPPERCASE" was introduced in DatabaseTestCase. This was introduced by this issue: #2802159: [D7] SQL layer: $match_operator is vulnerable to injection attack.
However this change was not tested against PostgreSQL database.
The issue with TEST_UPPERCASE seems to be that in PostgreSQL all identifiers are alway folded to lowercase. See: https://www.postgresql.org/docs/14/sql-syntax-lexical.html
Quoting an identifier also makes it case-sensitive, whereas unquoted names are always folded to lower case. For example, the identifiers FOO, foo, and "foo" are considered the same by PostgreSQL, but "Foo" and "FOO" are different from these three and each other.
And we use it this way (without quotes):
$sql = "CREATE TABLE {" . $name . "} (\n\t";
PostgreSQL test are failing now with an exception "SQLSTATE[42P07]: Duplicate table" (see: https://www.drupal.org/pift-ci-job/2310771).
Steps to reproduce
$schema = array(
'fields' => array(
'name' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
);
db_create_table('TEST_UPPERCASE', $schema);
print db_table_exists('TEST_UPPERCASE'); //returns false
print db_table_exists('test_uppercase'); //returns true
Proposed resolution
Remaining tasks
User interface changes
API changes
Data model changes
Release notes snippet
Issue fork drupal-3262341
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 3262341-d7-database-test changes, plain diff MR !1779
Comments
Comment #2
poker10 CreditAttribution: poker10 at ActivIT s.r.o. commentedComment #3
poker10 CreditAttribution: poker10 at ActivIT s.r.o. commentedAdding new related issue #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL as this problem seems to be not only with table names but also with all other identifiers.
Comment #6
Liam MorlandThe second commit broke other things; best to revert that. The first got rid of the TEST_UPPERCASE errors; it's now failing with other errors. That could be progress.
Comment #7
mcdruidI've not yet had a chance to review this thoroughly, but it looks like we're trying to fix the case sensitivity problem by introducing quoting of identifiers (or at least table names) throughout the pgsql driver?
I'm not sure that seems proportionate; there may be good reasons to introduce the quoting (e.g. it was added for MySQL 8 support, but made configurable) but just fixing this one test doesn't seem enough of a benefit to risk the regressions this might cause.
We could skip the problem test for PostgreSQL. Not a perfect solution, but less risky than making much more dramatic changes.
Or perhaps we could rewrite the test so that it doesn't fail for PostgreSQL?
Stating the obvious, it's hard working on PostgreSQL tests as overall the test suite doesn't run.
Was a similar test added to D8? If so, how does the PostreSQL driver handle that? (I've not checked if the D8/9 driver has quoting of identifiers, for example).
Comment #8
Liam MorlandThe problem is that installTables() creates the tables repeatedly. If a table exists, it delete it first. But db_table_exists(), which uses tableExists(), is case-sensitive. So, it is asked to create TEST_UPPERCASE. It actually creates test_uppercase, and when it runs next time, it says that TEST_UPPERCASE does not exist, so it tried to create test_uppercase again.
There doesn't seem to be a test like this in Drupal 9.3, so this doesn't get caught.
If db_create_table() folds to lower-case, perhaps db_table_exists() should as well. That would solve the immediate problem.
The TEST_UPPERCASE table was introduced as part of testing for SQL injection. Why this is needed is not mentioned in the code comments, commit message, or issue. It is mentioned on the parent issue of which this is a backport: #2492967: SQL layer: $match_operator is vulnerable to injection attack.
Since Postgres won't get uppercase table names anyway, perhaps this test should be skipped on Postgres.
I agree it is heavy-handed to add case-sensitive table name support just to fix this.
Comment #9
poker10 CreditAttribution: poker10 at ActivIT s.r.o. commentedI agree that this change could cause issues because it will change case insensitive behavior to case sensitive in PostgreSQL driver. So maybe it will be better to handle just this failing test.
If we can do this then all we need to do to have PostgreSQL tests run without unhandled exceptions is to fix this issue: #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL . My proposed patch attempts to keep the current behavior (the quotes are kept untouched), it only backports the fix to shorten identifiers from D9 (with a small adjustments).
Only then (when the tests will be running again) it should be possible to do also "bigger" changes, if needed.
But this issue needs to be fixed first if we want to see if the patch in #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL does not instroduce any new failures to existing PostgreSQL tests.
Comment #10
mcdruidCouple of examples where we skip certain parts of database tests for the sqlite driver:
https://git.drupalcode.org/project/drupal/-/blob/7.87/modules/simpletest...
https://git.drupalcode.org/project/drupal/-/blob/7.87/modules/simpletest...
I think we could do something similar for pgsql here. Here's a first pass at doing that.
I'll check into exactly what the uppercase test is all about though; I don't really remember all the details.
...is another option worth exploring.
Comment #11
Liam MorlandIn the second hunk the operator is backwards, should be
===
.if ($connection['driver'] !== 'pgsql') {
It could also test if the table exists instead of testing for the driver.
Otherwise, I think this will do what we need.
Comment #12
mcdruidOh - well spotted, thanks. Friday afternoon copypasta :)
Testing for the existence of the table's a good suggestion too, although perhaps it makes the "skip" less grep-able?
I may be missing something, but I'm not convinced that the UPPERCASE table name is actually significant other than exercising the filtering / validation of the operator a bit more thoroughly.
#2492967-35: SQL layer: $match_operator is vulnerable to injection attack is when it first appeared.
I'm doing a few experiments to see if the UPPERCASE-ness actually makes any difference.
Comment #13
mcdruidHmm looks like this test is still in 9.3.x AFAICS:
https://git.drupalcode.org/project/drupal/-/blob/9.3.5/core/tests/Drupal...
I'm not sure yet why it's not causing problems for the PostreSQL driver there; could be a difference in case sensitivity elsewhere like
db_create_table()
/db_table_exists()
(or their equivalents)?On the basis that the validation just does this:
https://git.drupalcode.org/project/drupal/-/blob/7.87/includes/database/...
...I still don't see how the uppercase table name is significant.
Comment #14
Liam MorlandYes, if someone is grepping for psql, they won't find this. I was thinking about it sort of like capability testing instead of version testing in JS. I could go either way on this.
As far as I saw, UPPERCASE_TEST is only used for that one SQL injection test.
The problem isn't the test; it's the creation of the tables with the deletion only being done if the table is found to exist. I could not find that code. The tables are created as part of a module install. That probably only happens once or if it happens more than once, any clean-up is taken care of by other code.
Comment #15
poker10 CreditAttribution: poker10 at ActivIT s.r.o. commentedYes, it is exactly as @Liam Morland pointed out.
The table "UPPERCASE_TEST" is created by this SQL statement:
CREATE TABLE {UPPERCASE_TEST}...
. This will create the table with a name "uppercase_test". When the test is installing the tables again:The
db_table_exists("UPPERCASE_TEST")
returns false, because the real table name is "uppercase_test". So the table is not dropped and the next db_create_table() will throw an exception.Comment #16
mcdruidThanks for the explanations.
Here's a patch taking that approach, which makes the DatabaseQueryTestCase pass for me locally (with PostgreSQL 9.5).
If this doesn't cause any problems, I think it's better than skipping the test(s).
Comment #17
poker10 CreditAttribution: poker10 at ActivIT s.r.o. commentedThe patch results seems good to me. All remaining unhandled exceptions in the testbot results are related to this issue #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL.
Comment #18
andypostthere's few coding standard fixes required
longer then 80 chars
needs empty line
Comment #19
mcdruidThanks for the review @andypost
#18.1 The same one-line comment is in the parent schema.inc and the mysql driver. I'd say that's one for a follow-up.
#18.2 Happy to add the extra line on commit. Thanks!
Comment #20
Fabianx CreditAttribution: Fabianx as a volunteer and at Tag1 Consulting commentedRTBC, looks good to me and makes sense
Comment #22
mcdruidThanks everyone!
With any luck, with this committed we should have tests running again for PostgreSQL (albeit with a few failures).
#710858: Meta issue: fix the remaining PostgreSQL bugs is the parent meta issue to work on those remaining problems.