@@ -1932,3 +1932,77 @@ 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 .IntegrityError as e :
1970+ if e .pgcode == errorcodes .UNIQUE_VIOLATION :
1971+ cr .rollback ()
1972+ raise ValueError ("The query for ids is producing duplicate values: {}" , query )
1973+ raise
1974+ self ._ncr = named_cursor (cr , itersize )
1975+ self ._ncr .execute (format_query (cr , "SELECT id FROM {} ORDER BY id" , self ._tmp_tbl ))
1976+ self ._it = iter (self ._ncr )
1977+
1978+ def _close (self ):
1979+ if self ._ncr :
1980+ if self ._ncr .closed :
1981+ return
1982+ self ._ncr .close ()
1983+ self ._cr .execute (format_query (self ._cr , "DROP TABLE IF EXISTS {}" , self ._tmp_tbl ))
1984+
1985+ def __len__ (self ):
1986+ return self ._len
1987+
1988+ def __iter__ (self ):
1989+ return self
1990+
1991+ def __next__ (self ):
1992+ if self ._ncr .closed :
1993+ raise StopIteration
1994+ try :
1995+ return next (self ._it )[0 ]
1996+ except StopIteration :
1997+ self ._close ()
1998+ raise
1999+
2000+ def __enter__ (self ):
2001+ return self
2002+
2003+ def __exit__ (self , exc_type , exc_value , traceback ):
2004+ self ._close ()
2005+ return False
2006+
2007+ def __del__ (self ):
2008+ self ._close ()
0 commit comments