launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #25782
[Merge] ~cjwatson/launchpad:stormify-potmsgset-queries into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:stormify-potmsgset-queries into launchpad:master.
Commit message:
Convert queries in lp.translations.model.potmsgset to Storm
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/394757
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-potmsgset-queries into launchpad:master.
diff --git a/lib/lp/translations/model/potmsgset.py b/lib/lp/translations/model/potmsgset.py
index f256384..649f6bd 100644
--- a/lib/lp/translations/model/potmsgset.py
+++ b/lib/lp/translations/model/potmsgset.py
@@ -1,4 +1,4 @@
-# Copyright 2009-2018 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
__metaclass__ = type
@@ -21,10 +21,17 @@ from sqlobject import (
StringCol,
)
from storm.expr import (
+ And,
Coalesce,
+ Column,
Desc,
+ Join,
+ Not,
Or,
+ Select,
SQL,
+ Table,
+ With,
)
from storm.store import (
EmptyResultSet,
@@ -39,11 +46,14 @@ from lp.services.config import config
from lp.services.database.constants import DEFAULT
from lp.services.database.interfaces import IStore
from lp.services.database.sqlbase import (
- cursor,
- quote,
SQLBase,
sqlvalues,
)
+from lp.services.database.stormexpr import (
+ IsTrue,
+ NullsFirst,
+ NullsLast,
+ )
from lp.services.helpers import shortlist
from lp.services.propertycache import get_property_cache
from lp.translations.interfaces.potmsgset import (
@@ -55,9 +65,6 @@ from lp.translations.interfaces.side import (
ITranslationSideTraitsSet,
TranslationSide,
)
-from lp.translations.interfaces.translationfileformat import (
- TranslationFileFormat,
- )
from lp.translations.interfaces.translationimporter import (
ITranslationImporter,
)
@@ -163,6 +170,8 @@ class POTMsgSet(SQLBase):
of all the source_file_format values. Otherwise, it should be
a `TranslationFileFormat` value.
"""
+ # Circular import.
+ from lp.translations.model.potemplate import POTemplate
translation_importer = getUtility(ITranslationImporter)
@@ -175,19 +184,18 @@ class POTMsgSet(SQLBase):
# Now let's find all the source_file_formats for all the
# POTemplates this POTMsgSet is part of.
- query = """
- SELECT DISTINCT POTemplate.source_file_format
- FROM TranslationTemplateItem
- JOIN POTemplate
- ON POTemplate.id = TranslationTemplateItem.potemplate
- WHERE TranslationTemplateItem.potmsgset = %s""" % (
- sqlvalues(self))
- cur = cursor()
- cur.execute(query)
- source_file_formats = cur.fetchall()
- for source_file_format, in source_file_formats:
+ origin = [
+ TranslationTemplateItem,
+ Join(
+ POTemplate,
+ TranslationTemplateItem.potemplate == POTemplate.id),
+ ]
+ source_file_formats = IStore(POTemplate).using(*origin).find(
+ POTemplate.source_file_format,
+ TranslationTemplateItem.potmsgset == self).config(distinct=True)
+ for source_file_format in source_file_formats:
format = translation_importer.getTranslationFormatImporter(
- TranslationFileFormat.items[source_file_format])
+ source_file_format)
format_uses_english_msgids = not format.uses_source_string_msgids
if uses_english_msgids is None:
@@ -326,15 +334,13 @@ class POTMsgSet(SQLBase):
include_dismissed=False,
include_unreviewed=True):
"""See `IPOTMsgSet`."""
- query = """
- is_current_ubuntu IS NOT TRUE AND
- is_current_upstream IS NOT TRUE AND
- potmsgset = %s AND
- language = %s
- """ % sqlvalues(self, language)
- msgstr_clause = make_plurals_sql_fragment(
- "msgstr%(form)d IS NOT NULL", "OR")
- query += " AND (%s)" % msgstr_clause
+ clauses = [
+ Not(IsTrue(TranslationMessage.is_current_ubuntu)),
+ Not(IsTrue(TranslationMessage.is_current_upstream)),
+ TranslationMessage.potmsgset == self,
+ TranslationMessage.language == language,
+ SQL(make_plurals_sql_fragment("msgstr%(form)d IS NOT NULL", "OR")),
+ ]
if include_dismissed != include_unreviewed:
current = self.getCurrentTranslation(
potemplate, language, potemplate.translation_side)
@@ -344,10 +350,10 @@ class POTMsgSet(SQLBase):
else:
comparing_date = current.date_reviewed
if include_unreviewed:
- term = " AND date_created > %s"
+ clause = TranslationMessage.date_created > comparing_date
else:
- term = " AND date_created <= %s"
- query += term % sqlvalues(comparing_date)
+ clause = TranslationMessage.date_created <= comparing_date
+ clauses.append(clause)
elif include_dismissed and include_unreviewed:
# Return all messages
pass
@@ -355,7 +361,7 @@ class POTMsgSet(SQLBase):
# No need to run a query.
return EmptyResultSet()
- return TranslationMessage.select(query)
+ return IStore(TranslationMessage).find(TranslationMessage, *clauses)
def _getExternalTranslationMessages(self, suggested_languages=(),
used_languages=()):
@@ -385,8 +391,9 @@ class POTMsgSet(SQLBase):
# Watch out when changing this condition: make sure it's done in
# a way so that indexes are indeed hit when the query is executed.
# Also note that there is a NOT(in_use_clause) index.
- in_use_clause = (
- "(is_current_ubuntu IS TRUE OR is_current_upstream IS TRUE)")
+ in_use_clause = Or(
+ IsTrue(TranslationMessage.is_current_ubuntu),
+ IsTrue(TranslationMessage.is_current_upstream))
# Present a list of language + usage constraints to sql. A language
# can either be unconstrained, used, or suggested depending on which
# of suggested_languages, used_languages it appears in.
@@ -397,26 +404,33 @@ class POTMsgSet(SQLBase):
used_languages = used_languages - both_languages
lang_used = []
if both_languages:
- lang_used.append('TranslationMessage.language IN %s' %
- quote(both_languages))
+ lang_used.append(
+ TranslationMessage.languageID.is_in(both_languages))
if used_languages:
- lang_used.append('(TranslationMessage.language IN %s AND %s)' % (
- quote(used_languages), in_use_clause))
+ lang_used.append(And(
+ TranslationMessage.languageID.is_in(used_languages),
+ in_use_clause))
if suggested_languages:
- lang_used.append(
- '(TranslationMessage.language IN %s AND NOT %s)' % (
- quote(suggested_languages), in_use_clause))
-
- msgsets = SQL('''msgsets AS (
- SELECT POTMsgSet.id
- FROM POTMsgSet
- JOIN TranslationTemplateItem ON
- TranslationTemplateItem.potmsgset = POTMsgSet.id
- JOIN SuggestivePOTemplate ON
- TranslationTemplateItem.potemplate =
- SuggestivePOTemplate.potemplate
- WHERE POTMsgSet.msgid_singular = %s and POTMsgSet.id <> %s
- )''' % sqlvalues(self.msgid_singular, self))
+ lang_used.append(And(
+ TranslationMessage.languageID.is_in(suggested_languages),
+ Not(in_use_clause)))
+
+ SuggestivePOTemplate = Table('SuggestivePOTemplate')
+ msgsets = With('msgsets', Select(
+ POTMsgSet.id,
+ tables=(
+ POTMsgSet,
+ Join(
+ TranslationTemplateItem,
+ TranslationTemplateItem.potmsgset == POTMsgSet.id),
+ Join(
+ SuggestivePOTemplate,
+ TranslationTemplateItem.potemplate ==
+ Column('potemplate', SuggestivePOTemplate))),
+ where=And(
+ POTMsgSet.msgid_singular == self.msgid_singular,
+ POTMsgSet.id != self.id)))
+ msgsets_table = Table('msgsets')
# Subquery to find the ids of TranslationMessages that are
# matching suggestions.
@@ -425,25 +439,23 @@ class POTMsgSet(SQLBase):
# excluding older messages that are identical to newer ones in
# all translated forms. The Python code can later sort out the
# distinct translations per form.
- msgstrs = ', '.join([
- 'COALESCE(msgstr%d, -1)' % form
- for form in range(TranslationConstants.MAX_PLURAL_FORMS)])
- ids_query_params = {
- 'msgstrs': msgstrs,
- 'where': '(' + ' OR '.join(lang_used) + ')',
- }
- ids_query = '''
- SELECT DISTINCT ON (%(msgstrs)s)
- TranslationMessage.id
- FROM TranslationMessage
- JOIN msgsets ON msgsets.id = TranslationMessage.potmsgset
- WHERE %(where)s
- ORDER BY %(msgstrs)s, date_created DESC
- ''' % ids_query_params
+ msgstrs = [
+ Coalesce(getattr(TranslationMessage, 'msgstr%dID' % form), -1)
+ for form in range(TranslationConstants.MAX_PLURAL_FORMS)]
result = IStore(TranslationMessage).with_(msgsets).find(
TranslationMessage,
- TranslationMessage.id.is_in(SQL(ids_query)))
+ TranslationMessage.id.is_in(Select(
+ TranslationMessage.id,
+ tables=(
+ TranslationMessage,
+ Join(
+ msgsets_table,
+ TranslationMessage.potmsgset ==
+ Column('id', msgsets_table))),
+ where=Or(*lang_used),
+ order_by=(msgstrs + [Desc(TranslationMessage.date_created)]),
+ distinct=msgstrs)))
return shortlist(result, longest_expected=100, hardlimit=2000)
@@ -556,17 +568,18 @@ class POTMsgSet(SQLBase):
:param prefer_shared: Whether to prefer a shared match over a
diverged one.
"""
- clauses = ['potmsgset = %s' % sqlvalues(self),
- 'language = %s' % sqlvalues(pofile.language),
- '(potemplate IS NULL OR potemplate = %s)' % sqlvalues(
- pofile.potemplate)]
+ clauses = [
+ TranslationMessage.potmsgset == self,
+ TranslationMessage.language == pofile.language,
+ Or(
+ TranslationMessage.potemplate == None,
+ TranslationMessage.potemplate == pofile.potemplate),
+ ]
for pluralform in range(pofile.plural_forms):
- if potranslations[pluralform] is None:
- clauses.append('msgstr%s IS NULL' % sqlvalues(pluralform))
- else:
- clauses.append('msgstr%s=%s' % (
- sqlvalues(pluralform, potranslations[pluralform])))
+ clauses.append(
+ getattr(TranslationMessage, 'msgstr%d' % pluralform) ==
+ potranslations[pluralform])
remaining_plural_forms = list(range(
pofile.plural_forms, TranslationConstants.MAX_PLURAL_FORMS))
@@ -574,18 +587,18 @@ class POTMsgSet(SQLBase):
# Prefer either shared or diverged messages, depending on
# arguments.
if prefer_shared:
- order = ['potemplate NULLS FIRST']
+ order = [NullsFirst(TranslationMessage.potemplateID)]
else:
- order = ['potemplate NULLS LAST']
+ order = [NullsLast(TranslationMessage.potemplateID)]
# Normally at most one message should match. But if there is
# more than one, prefer the one that adds the fewest extraneous
# plural forms.
order.extend([
- 'msgstr%s NULLS FIRST' % quote(form)
+ NullsFirst(getattr(TranslationMessage, 'msgstr%dID' % form))
for form in remaining_plural_forms])
- matches = list(
- TranslationMessage.select(' AND '.join(clauses), orderBy=order))
+ matches = list(IStore(TranslationMessage).find(
+ TranslationMessage, *clauses).order_by(*order))
if len(matches) > 0:
if len(matches) > 1:
@@ -1214,8 +1227,9 @@ class POTMsgSet(SQLBase):
def setSequence(self, potemplate, sequence):
"""See `IPOTMsgSet`."""
- translation_template_item = TranslationTemplateItem.selectOneBy(
- potmsgset=self, potemplate=potemplate)
+ translation_template_item = IStore(TranslationTemplateItem).find(
+ TranslationTemplateItem,
+ potmsgset=self, potemplate=potemplate).one()
if translation_template_item is not None:
# Update the sequence for the translation template item.
translation_template_item.sequence = sequence
@@ -1246,8 +1260,9 @@ class POTMsgSet(SQLBase):
def getSequence(self, potemplate):
"""See `IPOTMsgSet`."""
- translation_template_item = TranslationTemplateItem.selectOneBy(
- potmsgset=self, potemplate=potemplate)
+ translation_template_item = IStore(TranslationTemplateItem).find(
+ TranslationTemplateItem,
+ potmsgset=self, potemplate=potemplate).one()
if translation_template_item is not None:
return translation_template_item.sequence
else:
@@ -1260,5 +1275,5 @@ class POTMsgSet(SQLBase):
def getAllTranslationTemplateItems(self):
"""See `IPOTMsgSet`."""
- return TranslationTemplateItem.selectBy(
- potmsgset=self, orderBy=['id'])
+ return IStore(TranslationTemplateItem).find(
+ TranslationTemplateItem, potmsgset=self).order_by('id')