@@ -1932,3 +1932,83 @@ def bulk_update_table(cr, table, columns, mapping, key_col="id"):
19321932 key_col = key_col ,
19331933 )
19341934 cr .execute (query , [Json (mapping )])
1935+
1936+
1937+ class query_ids (object ):
1938+ """
1939+ Iterator over ids returned by a query.
1940+
1941+ This allows iteration over a potentially huge number of ids without exhausting memory.
1942+
1943+ :param str query: the query that returns the ids. It can be DML, e.g. `UPDATE table WHERE ... RETURNING id`.
1944+ :param int itersize: determines the number of rows fetched from PG at once, see :func:`~odoo.upgrade.util.pg.named_cursor`.
1945+ """
1946+
1947+ def __init__ (self , cr , query , itersize = None ):
1948+ self ._ncr = None
1949+ self ._cr = cr
1950+ self ._tmp_tbl = "_upgrade_query_ids_{}" .format (uuid .uuid4 ().hex )
1951+ cr .execute (
1952+ format_query (
1953+ cr ,
1954+ "CREATE UNLOGGED TABLE {}(id) AS (WITH query AS ({}) SELECT * FROM query)" ,
1955+ self ._tmp_tbl ,
1956+ SQLStr (query ),
1957+ )
1958+ )
1959+ self ._len = cr .rowcount
1960+ try :
1961+ cr .execute (
1962+ format_query (
1963+ cr ,
1964+ "ALTER TABLE {} ADD CONSTRAINT {} PRIMARY KEY (id)" ,
1965+ self ._tmp_tbl ,
1966+ "pk_{}_id" .format (self ._tmp_tbl ),
1967+ )
1968+ )
1969+ except psycopg2 .IntegrityError as e :
1970+ if e .pgcode == errorcodes .UNIQUE_VIOLATION :
1971+ raise ValueError ("The query for ids is producing duplicate values:\n {}" .format (query ))
1972+ raise
1973+ self ._ncr = named_cursor (cr , itersize )
1974+ self ._ncr .execute (format_query (cr , "SELECT id FROM {} ORDER BY id" , self ._tmp_tbl ))
1975+ self ._it = iter (self ._ncr )
1976+
1977+ def _close (self ):
1978+ if self ._ncr :
1979+ if self ._ncr .closed :
1980+ return
1981+ self ._ncr .close ()
1982+ try :
1983+ self ._cr .execute (format_query (self ._cr , "DROP TABLE IF EXISTS {}" , self ._tmp_tbl ))
1984+ except psycopg2 .InternalError as e :
1985+ if e .pgcode != errorcodes .IN_FAILED_SQL_TRANSACTION :
1986+ raise
1987+
1988+ def __len__ (self ):
1989+ return self ._len
1990+
1991+ def __iter__ (self ):
1992+ return self
1993+
1994+ def __next__ (self ):
1995+ if self ._ncr .closed :
1996+ raise StopIteration
1997+ try :
1998+ return next (self ._it )[0 ]
1999+ except StopIteration :
2000+ self ._close ()
2001+ raise
2002+
2003+ def next (self ):
2004+ return self .__next__ ()
2005+
2006+ def __enter__ (self ):
2007+ return self
2008+
2009+ def __exit__ (self , exc_type , exc_value , traceback ):
2010+ self ._close ()
2011+ return False
2012+
2013+ def __del__ (self ):
2014+ self ._close ()
0 commit comments