Skip to content
This repository has been archived by the owner on Feb 20, 2019. It is now read-only.

Database programming guidelines #3805

Closed
PVince81 opened this issue Feb 15, 2018 · 22 comments
Closed

Database programming guidelines #3805

PVince81 opened this issue Feb 15, 2018 · 22 comments

Comments

@PVince81
Copy link
Contributor

PVince81 commented Feb 15, 2018

  • always use the Query Builder

  • use IQueryBuilder::createPositionalParameter instead of IQueryBuilder::createNamedParameter when using like()

  • don't update more than 1 million rows within a transaction due to DB limitations

  • don't add more than 1000 conditions in a WHERE ... IN ... statement but chunk it into separate queries

  • when processing big tables, always do this in chunks, don't store the whole table in memory

  • Oracle compatibility specifics:

    • for Oracle "null" and empty strings are the same thing. Need special handling to catch these cases
      • when reading values, make sure to convert null to empty string when expected
      • keep this in mind when designing DB schema and values expectations.
      • when using a condition based on empty strings, use "is not null" with Oracle instead
    • Oracle can only compare the first 4000 bytes of a CLOB column
    • make sure to properly escape column names when using custom functions with createFunction. the escaping is usually done automatically by the query builder otherwise. Oracle is the most likely to complain about unquoted columns while other databases will work fine
  • always add the table name when calling lastInsertId($tableName), it is required by Oracle to return correct values

  • in general don't specify a value for autoincrement column. if you have to, keep in mind that Oracle's autoincrement trigger will get in the way on INSERT, so you'll need a subsequent UPDATE to properly adjust the value

  • always make sure there are unit tests for the database operations with queries to verify the result, this will help find out whether the database related code works on all databases and often times might reveal database quirks

  • running unit tests with specific databases: make test-php TEST_PHP_SUITE=path/to/test/file.php TEST_DATABASE=$databasetype where "$databasetype" is one of "sqlite", "mysql", "mariadb", "pgsql", "oci" and "mysqlmb4"

TODO: add more detail to all these

@DeepDiver1975
Copy link
Contributor

don't add more than 1000 conditions in a WHERE ... IN ... statement but chunk it into separate queries

999 as per sqlite

@PVince81
Copy link
Contributor Author

minus other conditions. so if you have where x=2 and y=3 and z in (...) then the IN part only gets 999-2=997 values

@VicDeo
Copy link
Contributor

VicDeo commented Feb 15, 2018

MySQL / InnoDB indexes are limited to 767 bytes - which is 191 char max for utf8mb4 encoding

@VicDeo
Copy link
Contributor

VicDeo commented Feb 15, 2018

string concatenation
CONCAT(str1, str2, ... strN) for Mysql
str1 || str1 ... || strN Sqlite/PgSQL/Oracle

@settermjd
Copy link
Contributor

Thanks for all the information, folks. I'll get the docs updated.

@settermjd
Copy link
Contributor

Is this documentation to be updated, or is a new section required?

@PVince81
Copy link
Contributor Author

PVince81 commented Apr 4, 2018

Sounds good, let's add it there maybe before all the code ?

@PVince81
Copy link
Contributor Author

PVince81 commented Apr 5, 2018

Added "- always add the table name when calling lastInsertId($tableName), it is required by Oracle to return correct values" in top post

@voroyam
Copy link
Contributor

voroyam commented Jul 12, 2018

So to sum up, @PVince81 you want something along those lines?

Database programming guidelines

- always use the Query Builder
- don't update more than 1 million rows within a transaction due to DB limitations
- don't add more than 1000 conditions in a WHERE ... IN ... statement but chunk it into separate queries
- don't add more than 999  conditions in a WHERE ... IN ... statement but chunk it into separate queries when using SQLite
- when processing big tables, always do this in chunks, don't store the whole table in memory
- Oracle compatibility specifics:
    - for Oracle "null" and empty strings are the same thing. Need special handling to catch these cases
        - when reading values, make sure to convert null to empty string when expected
        - keep this in mind when designing DB schema and values expectations.
        - when using a condition based on empty strings, use "is not null" with Oracle instead
    - Oracle can only compare the first 4000 bytes of a CLOB column
    - make sure to properly escape column names when using custom functions with `createFunction`. the escaping is usually done automatically by the query builder otherwise. Oracle is the most likely to complain about unquoted columns while other databases will work fine
- always add the table name when calling `lastInsertId($tableName)`, it is required by Oracle to return correct values
- in general don't specify a value for autoincrement column. if you have to, keep in mind that Oracle's autoincrement trigger will get in the way on INSERT, so you'll need a subsequent UPDATE to properly adjust the value
- *always* make sure there are unit tests for the database operations with queries to verify the result, this will help find out whether the database related code works on all databases and often times might reveal database quirks


- running unit tests with specific databases:  `make test-php TEST_PHP_SUITE=path/to/test/file.php TEST_DATABASE=$databasetype` where "$databasetype" is one of "sqlite", "mysql", "mariadb", "pgsql", "oci" and "mysqlmb4"

- string concatenation should be done like this:
- `CONCAT(str1, str2, ... strN)` for Mysql
- `str1 || str1 ... || strN` Sqlite/PgSQL/Oracle

and this should be added here:

image

@PVince81
Copy link
Contributor Author

@voroyam yes, would be great. Maybe it should rather appear after "Database Access" since accessing is the first step. Then we go into details about guidelines.

Is this something you'd like to put in yourself ?

@PVince81 PVince81 added this to the backlog milestone Jul 16, 2018
@voroyam
Copy link
Contributor

voroyam commented Jul 16, 2018

If no one else will - I could.

I'd rather let a developer or engineer write the documentation for developers because of my lack of knowledge. But the chances of that are close to zero :)

You, @DeepDiver1975 @VicDeo made the effort to write up the guidelines in this issue, so I thought if I just copy paste them and adjust the formatting, it would be fine.

What do you think? @PVince81

@PVince81
Copy link
Contributor Author

It should be possible to plan for someone to put it in, if everyone agrees that the guidelines as they are here make sense.

@DeepDiver1975
Copy link
Contributor

use IQueryBuilder::createPositionalParameter instead of IQueryBuilder::createNamedParameter when using like()

@settermjd
Copy link
Contributor

@voroyam, are you implementing this? Or has it already been done?

@voroyam
Copy link
Contributor

voroyam commented Sep 7, 2018

No, but I will.

@voroyam voroyam self-assigned this Sep 7, 2018
@voroyam
Copy link
Contributor

voroyam commented Sep 7, 2018

Question

Are those both statements correct?

* don't add more than 1000 conditions in a `WHERE ... IN ...` statement but chunk it into separate queries
* don't add more than 999 conditions in a `WHERE ... IN ...` statement but chunk it into separate queries when using SQLite

@PVince81 @DeepDiver1975

voroyam added a commit to owncloud/docs that referenced this issue Sep 7, 2018
This is the collection of guidelines for database work.

futher information can be found here

owncloud-archive/documentation#3805
@voroyam
Copy link
Contributor

voroyam commented Sep 7, 2018

owncloud/docs#71

@PVince81
Copy link
Contributor Author

PVince81 commented Sep 10, 2018

@voroyam let's make that a "999 minus number of other parameters", in a single statement, for SQLite (instead of two statements)

@voroyam
Copy link
Contributor

voroyam commented Sep 10, 2018

@PVince81

Those two lines in one line?

* Don't add more than 1000 conditions in a `WHERE ... IN ...` statement but chunk it into separate queries
* Don't add more than 999 conditions in a `WHERE ... IN ...` statement but chunk it into separate queries when using SQLite

in

* Don't add more than 999 conditions in a WHERE ... IN ... statement but chunk it into separate queries when using SQLite

@PVince81
Copy link
Contributor Author

@voroyam sounds good

@voroyam
Copy link
Contributor

voroyam commented Sep 11, 2018

@PVince81 done

@voroyam
Copy link
Contributor

voroyam commented Sep 17, 2018

PR merged, closing

@voroyam voroyam closed this as completed Sep 17, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants