Commit graph

415 commits

Author SHA1 Message Date
Tim Starling
191767b0c8 rdbms: Followup to upsert/replace parameter validation
Follows-up acd3a53799.

* Factor out duplicated code between upsert() and replace() into
  normalizeUpsertParams()
* Passing more than one key to normalizeUpsertKeys() was unconditionally
  an error, so simplify it by removing the loop and inspecting the count
  and first element.
* Rename $keyColumnCountNull to $numNulls
* Note that the complex code in assertValidUpsertSetArray() exists for
  the benefit of PostgreSQL.
* Rename makeConditionCollidesUponKey() to makeKeyCollisionCondition().
* Delete makeConditionCollidesUponKeys() since it was only ever called
  with a single key, so the callers can instead call
  makeKeyCollisionCondition().
* Rename $disjunctions to $orConds for clarity.

Change-Id: Ib40057ea1d9c69d5b7f061b6061c595dd3668454
2021-08-24 00:34:12 +00:00
Aaron Schulz
acd3a53799 rdbms: add unique key checks to upsert() and replace() in Database
Disallow changing unique key values of rows in the upsert() assignment.
The unique key values are used to decide which new and old rows collide.
Changing these values at the same time is too complex and ill-defined.

Disallow setting NULL values for columns of the identity key in upsert()
and replace(), unless they are all NULL for all of the rows. This edge
case can be treated as a plain INSERT, assuming that conflicts are not
triggered over weird DEFAULT column values.

Update the corresponding IDatabase documentation.

Change-Id: If6e0cecce32a5eedaf9a82b67ba41296bfa4a317
2021-08-18 19:18:40 -07:00
Timo Tijhof
9a7c213682 Remove trailing dot/space from some exception messages and output strings
One exception message contained a trailing dot/space, which I removed
as well, following I935835316c0.

A very small number of exceptions and output() calls contained trailing
space, which I removed for consistency.

Change-Id: I16f48c1a051c452bbef699eb9b7476d83f8821d8
2021-08-04 02:38:48 +00:00
James D. Forrester
1d71fd91d6 ILoadBalancer and IDatabase: Rename getMasterPos to getPrimaryPos
Bug: T282894
Change-Id: I30a600335af735a13a6ad2d3df56a5f17e05bfab
2021-08-02 18:14:41 +00:00
James D. Forrester
d11c59538a Rename DB primary position interfaces to DBPrimaryPos and MySQLPrimaryPos
And replace all uses.

Bug: T282894
Change-Id: I5222a8568255ac9fa5e2350e2264b8d2ee5eb968
2021-08-02 17:59:39 +00:00
Tim Starling
bc76602493 DBMS-specific ResultWrapper subclasses
Cleanup after the switch of Database::query() to return ResultWrapper
instead of resource.

* Soft-deprecate the IResultWrapper accessors in IDatabase.
* Move relevant DBMS-specific functionality to ResultWrapper subclasses.
  The deprecated methods in IResultWrapper become short and simple.
  ResultWrapper is now abstract (b/c break).
* Move the implementation of fieldName(), numFields() and one of the
  fieldInfo() implementations to the ResultWrapper subclass in order to
  avoid ResultWrapper::unwrap() calls.
* Make Database::doQuery() return a ResultWrapper subclass instead of
  underlying result data, so that the Database parent class does not
  need to be aware of wrapper construction.
* Hard-deprecate ResultWrapper::unwrap(),
  DatabaseMysqlBase::fieldType(), DatabasePostgres::fieldType().
* Fix the inefficient seeking method in SQLite.
* Make FakeResultWrapper extend ResultWrapper with an implementation
  similar to the SQLite one. This is possible because ResultWrapper does
  not depend on IDatabase anymore.
* Resolve fixme in DatabasePostgres: from studying the source,
  neither pg_fetch_object() nor pg_num_rows() can set an error
  retrievable with pg_last_error(). Removed unnecessary warning
  suppression.
* ResultWrapperTest didn't make sense as a unit test anymore, so I
  adapted it as an integration test against the current DBMS.

This change also means that ResultWrapper::key() always gives the
correct offset, even if Iterator methods are not being used.

Bug: T286694
Change-Id: I935835316c0bd7d3d061bd8fde9c9ce99ce756ec
2021-07-21 06:54:26 -07:00
Aaron Schulz
bfda754d35 rdbms: track the acquisition timestamps of named locks in Database
This is useful assigning timestamps to critical section operations
without the risk of second-level clock drift from application servers

Bug: T274174
Change-Id: I2fa8af3632f5edb608113b401c6089d8476b06dd
2021-07-19 12:10:48 -07:00
James D. Forrester
719cf161f2 More master -> primary documentation and internal var renaming
Bug: T254646
Change-Id: I63cc8895033714bdfbf09aee933a8f0a43b387f3
2021-07-15 11:20:20 +01:00
Tim Starling
9c3c0b704b Use array_fill_keys() instead of array_flip() if that reflects the developer's intention
array_fill_keys() was introduced in PHP 5.2.0 and works like
array_flip() except that it does only one thing (copying keys) instead
of two things (copying keys and values). That makes it faster and more
obvious.

When array_flip() calls were paired, I left them as is, because that
pattern is too cute. I couldn't kill something so cute.

Sometimes it was hard to figure out whether the values in array_flip()
result were used. That's the point of this change. If you use
array_fill_keys(), the intention is obvious.

Change-Id: If8d340a8bc816a15afec37e64f00106ae45e10ed
2021-06-15 00:11:10 +00:00
Tim Starling
f3dfcd73c7 Sanitize the function name when making an SQL comment
Because static::class or get_class($this) for an anonymous class
contains a null byte, causing non-MySQL DBMSes to give an error.

Change-Id: I58bec501d32335ad7d8514ec9c9be3c1920443f8
2021-06-08 17:03:57 +10:00
Petr Pchelko
0dfa846653 Use null coalecing operators everywhere consistenctly.
Auto-generated with rector.

Change-Id: I4f27e10cf029bb067b7bc57d82f7a64e21ea8d42
2021-06-03 21:42:06 -07:00
Gergő Tisza
fb22342dfa Dabatase: Assert that join conditions are arrays
Join conditions are in the form of [ <table> => [ 'JOIN', <cond> ] ].
If they are given incorrectly as strings, e.g. in the form
[ <table> => 'JOIN', <cond> ], there won't be any error (courtesy
of PHP handling list($a,$b)='x' by setting $a and $b to null) and
the resulting SQL will probably even be valid, just completely
different from what's intended. Be more strict about verifying
the format.

Change-Id: Id09aff6193829972cdca3262dc8bb878512c6cc3
2021-05-27 11:44:04 +02:00
jenkins-bot
064eea6483 Merge "rdbms: disallow upsert()/replace() calls with multiple unique keys" 2021-05-07 01:55:56 +00:00
Aaron Schulz
9ba3f4a2f5 rdbms: add an IDatabase method to expose DB server IDs
Bug: T274174
Change-Id: Ia81e64fa99154f43d27bfe070df03686eb4469c5
2021-05-05 14:38:19 -07:00
Aaron Schulz
99d5d2e8cc rdbms: cleanup getServer() and connection parameter fields in Database
Make getServer() always return a string, as documented, even with new
Database::NEW_UNCONNECTED handles that have yet to call open(). If the
'host' parameter to __construct() is ''/null, getServer() now returns
'localhost' instead of null. This avoids problems like fatal errors in
calls to TransactionProfiler::recordConnection().

Use Database constants for "connectionParams" field keys for better
static analysis.

Also:
* Add Database::getServerName() method that returns "readable" server
  names in the style of LoadBalancer::getServerName(). Note that the
  "hostName" field is already passed in from LoadBalancer.
* Migrate most getServer() callers to getServerName() for easier
  debugging and more readable logging.
* Also, normalize Database/LoadBalancer SPI logging context to use
  "db_server" and reduce logging code duplication in LoadBalancer.

Bug: T277056
Change-Id: I00ed4049ebb45edab1ea07561c47e226a423ea3b
2021-05-05 19:44:02 +00:00
Aaron Schulz
7cc4868758 rdbms: disallow upsert()/replace() calls with multiple unique keys
Change-Id: I4b6f07e8c8b3915c780885083f7465790328d2a3
2021-05-05 08:24:44 -07:00
Aaron Schulz
f39f0384f3 rdbms: remove whitespace padding from IDatabase::conditional()
Change-Id: Ib3d808966f8ea7a29fd503b0727373dae153d0ec
2021-04-27 11:03:09 -07:00
Aaron Schulz
1ffcb4ce8f rdbms: improve IDatabase::conditional() documentation
Change-Id: I33d403f89f4bbd6069cddda98c462f22990a5e4f
2021-04-27 11:01:20 -07:00
jenkins-bot
e8f0d25e29 Merge "rdbms: detect corrupt Database instances due to critical section failure" 2021-04-08 02:31:43 +00:00
Ammarpad
a4a954a626 rdbms: Fix property type documentations
Change-Id: Id4d3c74ef2e2f3b20573fd527ee9dabdf1d610a5
2021-03-28 21:19:13 +00:00
Aaron Schulz
278bb5c23e rdbms: detect corrupt Database instances due to critical section failure
This checks that the Database state has not diverged from the driver DB
handle nor server-side connection state due to an exception being thrown
in an unexpected place within internal Database methods. DB handles with
possible state corruption will not accept queries.

For example, a PHP extension like Excimer might be used to throw request
timeout exceptions. Such exceptions can trigger after any PHP or Zend
function returns, e.g. within DatabaseMysqlBase::doSelectDomain() after
the "USE" query completes but before $this->currentDomain gets updated.

Also:
* Make getApproximateLagStatus() catch getLag() errors since begin()
  expects it to simply use "false" for the lag value on failure. This
  helps assure that $this->trxAutomatic gets properly set.
* Unsuppress exceptions in runOnTransactionPreCommitCallbacks()
  as the transaction needs to get aborted anyway (as already happens).
* Unsuppress exceptions in runOnAtomicSectionCancelCallbacks() since
  the safest thing to do is just roll back the transaction.
* Only suppress DBError exceptions in runOnTransactionIdleCallbacks().
  and runTransactionListenerCallbacks(). Return the array of errors
  rather than throw the first one. Most of the callers had to catch
  the errors, so it's easier to avoid throwing them to begin with.
* Avoid blanket try/catch in sourceStream(), doReplace(), upsert(),
  doInsertSelectGenericand().
* Clarify various code comments and add missing @internal tags.

Bug: T193565
Change-Id: I6b7b02c02b24c2ff01094af3df54c989fe504af7
2021-03-24 14:25:50 +00:00
Aaron Schulz
383a5bcad9 rdbms: avoid undefined "expectBy" notices in TransactionProfiler (II)
Also:
* Add more documentation and phan annotations
* Avoid code duplication in resetExpectations()
* Make setExpectation() a bit more readable
* Move non-static field initialization to __construct()
* Convert two fields into simple class constants
* Mark TransactionProfiler with @internal
* Use reportExpectationViolated() in more cases in order to
  avoid similar logging code
* Make all transactionWritingOut() and recordQueryCompletion()
  arguments mandatory since the only callers already gave them

Bug: T269789
Bug: T277056
Change-Id: I6315e9c6a96f65343d45184a60d12665cbc32fc9
2021-03-16 03:58:11 +00:00
Bartosz Dziewoński
a8e44cceb2 Revert "rdbms: avoid undefined "expectBy" notices in TransactionProfiler"
This reverts commit 9624e4f74a.

Bug: T277056
Change-Id: I93cdac28edf3e4220269667fd244b907ef189726
2021-03-15 23:13:59 +01:00
Aaron Schulz
9624e4f74a rdbms: avoid undefined "expectBy" notices in TransactionProfiler
Also:
* Add more documentation and phan annotations
* Avoid code duplication in resetExpectations()
* Make setExpectation() a bit more readable
* Add more type hints to methods
* Move non-static field initialization to __construct()
* Convert two fields into simple class constants
* Mark TransactionProfiler with @internal
* Use reportExpectationViolated() in more cases in order to
  avoid similar logging code
* Make all transactionWritingOut() and recordQueryCompletion()
  arguments mandatory since the only callers already gave them

Bug: T269789
Change-Id: I5d106ac5c95d0ea94c4f6bdee90ca51f8f7ddbad
2021-03-09 16:59:52 +00:00
Aaron Schulz
99a0954aad rdbms: fix some PHP 8 warnings in Database/LoadBalancer/LBFactory
Several PHP core methods now warn about null values

Change-Id: I5d5e111b93b882e4e00479ca3b16a16b89fbbde4
2021-03-09 16:48:38 +00:00
Timo Tijhof
26c9849330 docs: Fix 'dependant' typos
The intended word in all these cases was the adjective "dependent".

Whilst the "dependant" does exist, it is a noun and generally
refers to a person. The word is rare used in general, but
especially so in a technology context.

Change-Id: Ic7e2d2ea6a566f4139ff1fdb77f38b0e962ccd9c
2021-02-18 16:59:20 +00:00
Umherirrender
8de3b7d324 Use static closures where safe to use
This is micro-optimization of closure code to avoid binding the closure
to $this where it is not needed.

Created by I25a17fb22b6b669e817317a0f45051ae9c608208

Change-Id: I0ffc6200f6c6693d78a3151cb8cea7dce7c21653
2021-02-11 00:13:52 +00:00
Thiemo Kreuz
50f8929efa Fix documentation of IDatabase::selectField()
I found code in an extension that actually does this and
provides an array of field names (obviously with a single
field name only). This might be an "unintentional feature".
Still I believe it's a good idea to support it, because all
other methods support it as well. This makes selectField less
of an outlier.

Change-Id: Ie63b06896b59dcaa629f720f98f28943674dc8d7
2021-02-05 08:01:44 +00:00
Umherirrender
62002cdcf1 build: Update mediawiki/mediawiki-codesniffer to 35.0.0
Change-Id: Idb413be4b8cba8611afdc022af59810ce1a4531e
2021-01-31 13:34:38 +00:00
Timo Tijhof
6404da3d39 rdbms: Remove outdated MySQL 4 references and fix doc URLs
- Remove or clarify ancient comments referencing MySQL 4.

- Oracle can't afford to host small HTML files for longer than two
  years, as such they make a point to break their URLs after a while.
  Update our references to the last 5.x release for now, where the same
  information resides (instead 404 or redirect to MySQL 8 docs).

Bug: T34217
Change-Id: I322cc8936f96c2545c63e7c9c1f5fa241e2b8c18
2021-01-30 22:47:33 +00:00
Ammarpad
10e58e2637 rbdms: Improve signature of Database::getCacheSetOptions
One of the passed args is allowed to be null, but this null will
just be ignored and does not seems to be useful here so this is
now deprecated as suggested by Krinle at Id837e81

Now the function takes variadic args, which may still contain null
for sometime (owing to the method being public api).  In 1.37+, the
variadic parameter can be typehinted, and then the check for
instanceof would no longer be necessary.

Change-Id: I3863c3981470c1dbc11e417bc2252339bdee391f
2021-01-18 07:07:52 +01:00
Aaron Schulz
f5dea84dfa rdbms: split out LoadBalancer::fieldHasBit to reduce code duplication
Also clean up Database::fieldHasBit parameter names

Change-Id: I9e94d7fff87e98e78e9a62672d4e81b812fd9a79
2021-01-11 19:09:50 -08:00
DannyS712
7cc3d0c240 Minor cleanup in Database.php, should be a no-op
- Some whitespace cleanup

- Use early returns in ::getClass()

- Declare $rtt in ::pingAndCalculateLastTrxApplyTime()

- Make error messages for selecting the '*' $var field
  when not allowed consistent

- Flip order of $first handling in ::makeList()

Change-Id: Ibda4b72157986b0c7e6e03fb9f49836bc9b018c9
2021-01-06 06:08:20 +00:00
Ammarpad
0a4cd7c671 Restore use of func_get_args() in getCacheSetOptions()
The documentation says this can be used "for several DBs" but
because there are only two real paramaters and the gotcha  explained
at I7d4804a, I unintentionally limited it to work for "only two"
databases while fixing a different issue.

I am not sure wether there are callers calling with 5, 10 or 20
database objects, but PHP will never raise any warning even if
there are, the remaining args will just be silently discarded.

This restores the behavior so it can work "for several DBs", as
it used to.

Follow-up: I7d4804a715063d95827fcec6d14a3ae059b234c3
Change-Id: Id837e813cda19b86dcd14ec36b520f8465e5855d
2021-01-04 07:47:14 +00:00
jenkins-bot
99a6d07f90 Merge "Prevent calling database method on null" 2021-01-03 04:20:25 +00:00
Ammarpad
9b887ac1cc Prevent calling database method on null
It's allowed to call Database::getCacheSetOptions() with null
as a second argument since the paramater is nullable.

However, this will lead to fatal error as we will end up calling
getSessionLagStatus() on the passed null.

The only minor safeguard to this now is if the second argument is
omitted, then func_get_args() will not contain the default as it
returns only a copy of the passed arguments, but this is not
something that should be relied upon. There's nothing that can
prevent calling code from passing the second null.

Also pass the arguments directly to foreach() without
calling func_get_args() since we already know the possible
passable arguments and the function is not variadic.

Change-Id: I7d4804a715063d95827fcec6d14a3ae059b234c3
2021-01-03 04:16:28 +01:00
Ammarpad
87ecf20aa7 Fix documentation in Database class
Empty condition is also disallowed for DELETE query.

Change-Id: I7c7b24821fa32b9edaf5026134ce745913350766
2021-01-03 03:33:38 +01:00
Ammarpad
9efb07c9d2 Use short array syntax to add array element
The short syntax is faster and recommended over the method call

Change-Id: I18a3adcfa66abe5eadb834c0b23b54c6853b8c91
2021-01-02 08:20:24 +01:00
Aaron Schulz
e3237d4b1d rdbms: rename transaction idle/precommit/postcommit callback fields in Database
This better conveys when the callbacks are supposed to be fired

Change-Id: If4a5bb3cce510657b03d1891cfb77608a0cc61b2
2020-12-13 21:15:28 +00:00
Daimona Eaytoy
b18344ba1e Clarify error message in Database::assertBuildSubstringParams
Change-Id: Iba115a6c514b49b02c2073ebfc81b6fc2ec117e9
2020-12-09 02:38:31 +01:00
Umherirrender
c85a43561e Improve class property documentation
Reformat existing documentation to match the format

Change-Id: I190b54b5e962f17bab6502dd1b3c02f11dc926d2
2020-10-30 10:38:58 +01:00
Umherirrender
d790580fda Fix typos related to repeated words
Change-Id: Ibc187d95b003017255bc87adf56afae7a59bd3db
2020-09-27 10:25:36 +00:00
Máté Szabó
ce50e1bff9 Permit temporary table writes on replica DB connections
In I8e17644d1b447416adee18e42cf0122b52a80b22, MediaWiki's DBAL was adjusted to
reject any write query on read-only DB replica connections. This poses a problem
for extensions that use temporary tables in their queries, as such queries now
have to be executed on the source DB rather than a replica to work around this
fact. An example of such an extension is Semantic MediaWiki, whose QueryEngine
uses temporary tables extensively in serving reads. The current situation, where
all writes, including non-persistent ones, must be executed on a source DB
connection, causes scalability issues since it's no longer possible to
distribute these queries between multiple replicas.

An old code comment in the DBAL cited MySQL bug 33669 as a potential blocker to
permitting temporary table operations on read-only connections. However, that
bug was closed a decade ago, and Fandom's Semantic MediaWiki cluster has been
permitting such operations on its MySQL 5.7 replica nodes (running with
--read-only) for several years now, without observing any adverse side-effect.

This patch accordingly relaxes the restrictions placed by the MediaWiki DBAL on
temporary table operations to enable executing them even on read-only replica DB
connections. Several unit tests were added to verify the conditions under which
a given write query may be allowed to execute on a connection.

Bug: T259362
Change-Id: I90a1427a15d0aee07e7b24ba4248b7ef4475c227
2020-07-31 17:47:06 +02:00
Umherirrender
c417320dd2 Fix Database::getTempTableWrites for multi table DDLs
Capture group with * does not return a array, only the last match.
Match all and split on the delimiter instead

Bug: T252183
Change-Id: I39701dd367bf2914e7c6fb24b3f14f09a059e483
2020-07-28 17:50:17 +02:00
Kunal Mehta
0b43c49465 Revert "Add a new type of database to the installer from extension"
It caused a 20% latency regression by unconditionally parsing extension.json
files on every single load instead of using the existing caching
infrastructure. There are further problems with the use of parsing/loading
extension.json files in a method that is incompatible with the existing
architecture.

This primarily reverts commit 46eabe275c.

Also needed to revert 16381261ae and 7c72347ec1.

Bug: T258664
Change-Id: I34a783c3f0df0447876a26441bb2d12e02368871
2020-07-22 16:05:31 -07:00
Aaron Schulz
5aad68403c rdbms: improve Database::commit() "out of sync" logging
Bug: T256394
Change-Id: If716a34bcd1d6fc10103ddcb3c19deb97cdacf17
2020-07-16 18:00:16 -07:00
daniel
3395978eb8 rdbms: Mark the Database class as stable for subclassing
Strategy used to determine which methods should be stable:
- Methods that encode specific SQL syntax
- Methods already overwritten by a subclass

Strategy used to determine which methods should NOT be stable:
- private or final (obviously)
- encodes logic for managing database connections
- wraps a call to a method that is intended for overriding

Note that such methods would be overwritten by an extension that
provides support for a specific database backend. Such extensions
are only now becoming possible in 1.35. Marking methods in Database
as stable for overriding is done to support the creation of
such extension (e.g. for MSSQL and Oracle).

Bug: T247862
Change-Id: Ief5386c0ff4239a8d95db2d6896338612aa56bb0
2020-07-13 19:26:46 +02:00
jenkins-bot
8b2f44b6e7 Merge "phan: Enable redundant_condition_detection" 2020-07-02 00:28:10 +00:00
Umherirrender
bc5cb7ae64 phan: Enable redundant_condition_detection
Remove duplicate casts
Suppress false positives

Bug: T248438
Change-Id: I2f89664a4bcd3b39b15e7cf850adda2f0c90ae6f
2020-07-01 20:13:07 +00:00
ArtBaltai
46eabe275c Add a new type of database to the installer from extension
Decouple Installer services
Implement injection class Autoloader and i18n messages from extension.json
Implement extension selector by type
Add i18n message key `version-database`
Extensions for testing:
- https://github.com/MWStake/PerconaDB - real Percona extension
- https://github.com/killev/mediawiki-dbext2 - fake extension for test

Bug: T226857, T255151
Change-Id: I9ec8a18ad19283f6be67ac000110ac370afc0815
2020-06-26 13:37:32 +03:00