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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
- 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
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
- 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
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
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
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
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
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
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
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