@@ -1932,3 +1932,75 @@ 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 iterator can memory efficiently query a potentially huge number of ids.
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: passed to a named_cursor, determines the number of rows fetched from PG at once.
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 .errors .UniqueViolation :
1970+ cr .rollback ()
1971+ raise ValueError ("The query for ids is producing duplicate values: {}" , query )
1972+ self ._ncr = named_cursor (cr , itersize )
1973+ self ._ncr .execute (format_query (cr , "SELECT id FROM {} ORDER BY id" , self ._tmp_tbl ))
1974+ self ._it = iter (self ._ncr )
1975+
1976+ def _close (self ):
1977+ if self ._ncr :
1978+ if self ._ncr .closed :
1979+ return
1980+ self ._ncr .close ()
1981+ self ._cr .execute (format_query (self ._cr , "DROP TABLE IF EXISTS {}" , self ._tmp_tbl ))
1982+
1983+ def __len__ (self ):
1984+ return self ._len
1985+
1986+ def __iter__ (self ):
1987+ return self
1988+
1989+ def __next__ (self ):
1990+ if self ._ncr .closed :
1991+ raise StopIteration
1992+ try :
1993+ return next (self ._it )[0 ]
1994+ except StopIteration :
1995+ self ._close ()
1996+ raise
1997+
1998+ def __enter__ (self ):
1999+ return self
2000+
2001+ def __exit__ (self , exc_type , exc_value , traceback ):
2002+ self ._close ()
2003+ return False
2004+
2005+ def __del__ (self ):
2006+ self ._close ()
0 commit comments