1111
1212import logging
1313import re
14+ import uuid
1415from contextlib import contextmanager
1516from functools import wraps
1617from itertools import chain
@@ -374,7 +375,8 @@ class iter_browse(object):
374375
375376 :param model: the model to iterate
376377 :type model: :class:`odoo.model.Model`
377- :param list(int) ids: list of IDs of the records to iterate
378+ :param iterable(int) or SQLStr ids: iterable of IDs of the records to iterate, or a SQL query
379+ that can produce the IDs
378380 :param int chunk_size: number of records to load in each iteration chunk, `200` by
379381 default
380382 :param logger: logger used to report the progress, by default
@@ -387,23 +389,53 @@ class iter_browse(object):
387389 See also :func:`~odoo.upgrade.util.orm.env`
388390 """
389391
390- __slots__ = ("_chunk_size" , "_cr_uid" , "_it" , "_logger" , "_model" , "_patch" , "_size" , "_strategy" )
392+ __slots__ = ("_chunk_size" , "_cr_uid" , "_ids" , " _it" , "_logger" , "_model" , "_patch" , "_size" , "_strategy" )
391393
392394 def __init__ (self , model , * args , ** kw ):
393395 assert len (args ) in [1 , 3 ] # either (cr, uid, ids) or (ids,)
394396 self ._model = model
395397 self ._cr_uid = args [:- 1 ]
396- ids = args [- 1 ]
397- self ._size = len ( ids )
398+ self . _ids = args [- 1 ]
399+ self ._size = kw . pop ( "size" , None )
398400 self ._chunk_size = kw .pop ("chunk_size" , 200 ) # keyword-only argument
399401 self ._logger = kw .pop ("logger" , _logger )
400402 self ._strategy = kw .pop ("strategy" , "flush" )
401403 assert self ._strategy in {"flush" , "commit" }
402404 if kw :
403405 raise TypeError ("Unknown arguments: %s" % ", " .join (kw ))
404406
407+ if isinstance (self ._ids , SQLStr ):
408+ self ._init_ids_query ()
409+
410+ if not self ._size :
411+ try :
412+ self ._size = len (self ._ids )
413+ except TypeError :
414+ raise ValueError ("When passing ids as a generator, the size kwarg is mandatory" )
405415 self ._patch = None
406- self ._it = chunks (ids , self ._chunk_size , fmt = self ._browse )
416+ self ._it = chunks (self ._ids , self ._chunk_size , fmt = self ._browse )
417+
418+ def _init_ids_query (self ):
419+ cr = self ._model .env .cr
420+ tmp_tbl = "_upgrade_ib_{}" .format (uuid .uuid4 ().hex )
421+ cr .execute (
422+ format_query (
423+ cr , "CREATE UNLOGGED TABLE {}(id) AS (WITH query AS ({}) SELECT * FROM query)" , tmp_tbl , self ._ids
424+ )
425+ )
426+ self ._size = cr .rowcount
427+ cr .execute (
428+ format_query (cr , "ALTER TABLE {} ADD CONSTRAINT {} PRIMARY KEY (id)" , tmp_tbl , "pk_{}_id" .format (tmp_tbl ))
429+ )
430+
431+ def get_ids ():
432+ with named_cursor (cr , itersize = self ._chunk_size ) as ncr :
433+ ncr .execute (format_query (cr , "SELECT id FROM {} ORDER BY id" , tmp_tbl ))
434+ for (id_ ,) in ncr :
435+ yield id_
436+ cr .execute (format_query (cr , "DROP TABLE IF EXISTS {}" , tmp_tbl ))
437+
438+ self ._ids = get_ids ()
407439
408440 def _browse (self , ids ):
409441 next (self ._end (), None )
0 commit comments