seitime-frappe/frappe/database/sequence.py

101 lines
2.7 KiB
Python

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})",
}
)