from frappe import db, scrub # NOTE: # FOR MARIADB - using no cache - as during backup, if the sequence was used in anyform, # it drops the cache and uses the next non cached value in setval query and # puts that in the backup file, which will start the counter # from that value when inserting any new record in the doctype. # By default the cache is 1000 which will mess up the sequence when # using the system after a restore. # # Another case could be if the cached values expire then also there is a chance of # the cache being skipped. # # FOR POSTGRES - The sequence cache for postgres is per connection. # Since we're opening and closing connections for every request this results in skipping the cache # to the next non-cached value hence not using cache in postgres. # ref: https://stackoverflow.com/questions/21356375/postgres-9-0-4-sequence-skipping-numbers SEQUENCE_CACHE = 0 def create_sequence( doctype_name: str, *, slug: str = "_id_seq", temporary: bool = False, check_not_exists: bool = False, cycle: bool = False, cache: int = SEQUENCE_CACHE, start_value: int = 0, increment_by: int = 0, min_value: int = 0, max_value: int = 0, ) -> str: query = "create sequence" if not temporary else "create temporary sequence" sequence_name = scrub(doctype_name + slug) if check_not_exists: query += " if not exists" query += f" {sequence_name}" if increment_by: # default is 1 query += f" increment by {increment_by}" if min_value: # default is 1 query += f" minvalue {min_value}" if max_value: query += f" maxvalue {max_value}" if start_value: # default is 1 query += f" start {start_value}" # in postgres, the default is cache 1 / no cache if cache: query += f" cache {cache}" elif db.db_type == "mariadb": query += " nocache" if not cycle: # in postgres, default is no cycle if db.db_type == "mariadb": query += " nocycle" else: query += " cycle" db.sql_ddl(query) return sequence_name def get_next_val(doctype_name: str, slug: str = "_id_seq") -> int: sequence_name = scrub(f"{doctype_name}{slug}") if db.db_type == "postgres": sequence_name = f"'\"{sequence_name}\"'" elif db.db_type == "mariadb": sequence_name = f"`{sequence_name}`" try: return db.sql(f"SELECT nextval({sequence_name})")[0][0] except IndexError: raise db.SequenceGeneratorLimitExceeded def set_next_val( doctype_name: str, next_val: int, *, slug: str = "_id_seq", is_val_used: bool = False ) -> None: is_val_used = "false" if not is_val_used else "true" db.multisql( { "postgres": f"SELECT SETVAL('\"{scrub(doctype_name + slug)}\"', {next_val}, {is_val_used})", "mariadb": f"SELECT SETVAL(`{scrub(doctype_name + slug)}`, {next_val}, {is_val_used})", } )