Page MenuHomePhabricator

PostgreSQL timestamp comparison throws exception with user input
Closed, DuplicatePublic

Description

PostgreSQL uses TIMESTAMPTZ for timestamp fields instead of CHAR(14). Any comparison between these timestamp fields and a string literal requires the literal to be convertible to a timestamp, or else a query error occurs and an exception is thrown.

For example, pretty much every IndexPager will throw an exception on PostgreSQL if you set the offset parameter to something other than a timestamp, using a URL like http://localhost/w/index.php?title=Main_Page&action=history&offset=1 . This generates the error:

Error 22007: ERROR: invalid input syntax for type timestamp with time zone: "1"
LINE 1: ...user)) WHERE rev_page = 1 AND (rev_timestamp >= '1') ORDE...
^

Function: MediaWiki\Pager\IndexPager::buildQueryInfo (history page unfiltered)
Query: SELECT rev_id,rev_page,rev_timestamp,rev_minor_edit,rev_deleted,rev_len,rev_parent_id,rev_sha1,actor_rev_user.actor_user AS "rev_user",actor_rev_user.actor_name AS "rev_user_text",rev_actor,comment_rev_comment.comment_text AS "rev_comment_text",comment_rev_comment.comment_data AS "rev_comment_data",comment_rev_comment.comment_id AS "rev_comment_cid",user_name,(SELECT array_to_string(array_agg(ctd_name),',') FROM "change_tag" JOIN "change_tag_def" ON ((ct_tag_id=ctd_id)) WHERE ct_rev_id=rev_id ) AS "ts_tags" FROM "revision" JOIN "actor" "actor_rev_user" ON ((actor_rev_user.actor_id = rev_actor)) JOIN "comment" "comment_rev_comment" ON ((comment_rev_comment.comment_id = rev_comment_id)) LEFT JOIN "user" ON ((actor_rev_user.actor_user != 0) AND (user_id = actor_rev_user.actor_user)) WHERE rev_page = 1 AND (rev_timestamp >= '1') ORDER BY rev_timestamp,rev_id LIMIT 1

We noticed this in BlockListPager while fixing T352310.

Note that a conventional 14-char timestamp is not a valid PG timestamp, so any client that constructs its own offset parameter, following normal MediaWiki conventions, will cause an exception in the server.

The simplest way to fix this would be to convert all the TIMESTAMPTZ fields to CHAR or VARCHAR for consistency with MySQL.

Event Timeline

Already reported at T345793 which I intended to fix but forgot. My approach in the uncommitted change was to convert the user-provided offset to DB timestamp and proceed if it works, otherwsise return no result (as does MySQL currently). That seems easier for the short term, but changing the field would be more effective long-term.