Merge pull request #15906 from ankush/ignore_duplicates_db
feat(db/pg): ability to ignore pk collision
This commit is contained in:
commit
c38d1f8a25
4 changed files with 37 additions and 13 deletions
|
|
@ -177,6 +177,8 @@ class Database(object):
|
|||
raise frappe.QueryTimeoutError(e)
|
||||
|
||||
elif frappe.conf.db_type == 'postgres':
|
||||
# TODO: added temporarily
|
||||
print(e)
|
||||
raise
|
||||
|
||||
if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)):
|
||||
|
|
|
|||
|
|
@ -389,12 +389,24 @@ class BaseDocument(object):
|
|||
fieldname = [df.fieldname for df in self.meta.get_table_fields() if df.options==doctype]
|
||||
return fieldname[0] if fieldname else None
|
||||
|
||||
def db_insert(self):
|
||||
"""INSERT the document (with valid columns) in the database."""
|
||||
def db_insert(self, ignore_if_duplicate=False):
|
||||
"""INSERT the document (with valid columns) in the database.
|
||||
|
||||
args:
|
||||
ignore_if_duplicate: ignore primary key collision
|
||||
at database level (postgres)
|
||||
in python (mariadb)
|
||||
"""
|
||||
if not self.name:
|
||||
# name will be set by document class in most cases
|
||||
set_new_name(self)
|
||||
|
||||
conflict_handler = ""
|
||||
# On postgres we can't implcitly ignore PK collision
|
||||
# So instruct pg to ignore `name` field conflicts
|
||||
if ignore_if_duplicate and frappe.db.db_type == "postgres":
|
||||
conflict_handler = "on conflict (name) do nothing"
|
||||
|
||||
if not self.creation:
|
||||
self.creation = self.modified = now()
|
||||
self.created_by = self.modified_by = frappe.session.user
|
||||
|
|
@ -405,10 +417,11 @@ class BaseDocument(object):
|
|||
columns = list(d)
|
||||
try:
|
||||
frappe.db.sql("""INSERT INTO `tab{doctype}` ({columns})
|
||||
VALUES ({values})""".format(
|
||||
doctype = self.doctype,
|
||||
columns = ", ".join("`"+c+"`" for c in columns),
|
||||
values = ", ".join(["%s"] * len(columns))
|
||||
VALUES ({values}) {conflict_handler}""".format(
|
||||
doctype=self.doctype,
|
||||
columns=", ".join("`"+c+"`" for c in columns),
|
||||
values=", ".join(["%s"] * len(columns)),
|
||||
conflict_handler=conflict_handler
|
||||
), list(d.values()))
|
||||
except Exception as e:
|
||||
if frappe.db.is_primary_key_violation(e):
|
||||
|
|
@ -421,8 +434,11 @@ class BaseDocument(object):
|
|||
self.db_insert()
|
||||
return
|
||||
|
||||
frappe.msgprint(_("{0} {1} already exists").format(self.doctype, frappe.bold(self.name)), title=_("Duplicate Name"), indicator="red")
|
||||
raise frappe.DuplicateEntryError(self.doctype, self.name, e)
|
||||
if not ignore_if_duplicate:
|
||||
frappe.msgprint(_("{0} {1} already exists")
|
||||
.format(self.doctype, frappe.bold(self.name)),
|
||||
title=_("Duplicate Name"), indicator="red")
|
||||
raise frappe.DuplicateEntryError(self.doctype, self.name, e)
|
||||
|
||||
elif frappe.db.is_unique_key_violation(e):
|
||||
# unique constraint
|
||||
|
|
|
|||
|
|
@ -249,11 +249,7 @@ class Document(BaseDocument):
|
|||
if getattr(self.meta, "issingle", 0):
|
||||
self.update_single(self.get_valid_dict())
|
||||
else:
|
||||
try:
|
||||
self.db_insert()
|
||||
except frappe.DuplicateEntryError as e:
|
||||
if not ignore_if_duplicate:
|
||||
raise e
|
||||
self.db_insert(ignore_if_duplicate=ignore_if_duplicate)
|
||||
|
||||
# children
|
||||
for d in self.get_all_children():
|
||||
|
|
|
|||
|
|
@ -291,6 +291,16 @@ class TestDB(unittest.TestCase):
|
|||
|
||||
frappe.db.MAX_WRITES_PER_TRANSACTION = Database.MAX_WRITES_PER_TRANSACTION
|
||||
|
||||
def test_pk_collision_ignoring(self):
|
||||
# note has `name` generated from title
|
||||
for _ in range(3):
|
||||
frappe.get_doc(doctype="Note", title="duplicate name").insert(ignore_if_duplicate=True)
|
||||
|
||||
with savepoint():
|
||||
self.assertRaises(frappe.DuplicateEntryError, frappe.get_doc(doctype="Note", title="duplicate name").insert)
|
||||
# recover transaction to continue other tests
|
||||
raise Exception
|
||||
|
||||
|
||||
@run_only_if(db_type_is.MARIADB)
|
||||
class TestDDLCommandsMaria(unittest.TestCase):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue