diff --git a/frappe/database/query.py b/frappe/database/query.py index 30bec1097e..c8cf862e54 100644 --- a/frappe/database/query.py +++ b/frappe/database/query.py @@ -134,10 +134,6 @@ TAB_PATTERN = re.compile("^tab") WORDS_PATTERN = re.compile(r"\w+") COMMA_PATTERN = re.compile(r",\s*(?![^()]*\))") -# less restrictive version of frappe.core.doctype.doctype.doctype.START_WITH_LETTERS_PATTERN -# to allow table names like __Auth -TABLE_NAME_PATTERN = re.compile(r"^[\w -]*$", flags=re.ASCII) - # Pattern for validating simple field names (alphanumeric + underscore) SIMPLE_FIELD_PATTERN = re.compile(r"^\w+$", flags=re.ASCII) @@ -272,7 +268,6 @@ class Engine: self.doctype = get_doctype_name(table.get_sql()) else: self.doctype = table - self.validate_doctype() self.table = qb.DocType(table) if self.apply_permissions: @@ -340,10 +335,6 @@ class Engine: self.query.immutable = True return self.query - def validate_doctype(self): - if not TABLE_NAME_PATTERN.match(self.doctype): - frappe.throw(_("Invalid DocType: {0}").format(self.doctype)) - def apply_fields(self, fields): self.fields = self.parse_fields(fields) @@ -1037,11 +1028,6 @@ class Engine: field_name = groups[3] # This will be the field name (e.g., 'field') if table_name: - # Table name specified (e.g., `tabX`.`y` or tabX.y or `tabX Y`.`y`) - # Ensure the extracted table name is valid before creating DocType object - if not TABLE_NAME_PATTERN.match(table_name.lstrip("tab")): - frappe.throw(_("Invalid characters in table name: {0}").format(table_name)) - doctype_name = table_name[3:] if table_name.startswith("tab") else table_name table_obj = frappe.qb.DocType(doctype_name) pypika_field = table_obj[field_name] diff --git a/frappe/query_builder/builder.py b/frappe/query_builder/builder.py index 0b4a2b48bb..76648bd896 100644 --- a/frappe/query_builder/builder.py +++ b/frappe/query_builder/builder.py @@ -1,3 +1,4 @@ +import re import types import typing @@ -9,6 +10,10 @@ from pypika.terms import Function from frappe.query_builder.terms import ParameterizedValueWrapper, SQLiteParameterizedValueWrapper from frappe.utils import get_table_name +# less restrictive version of frappe.core.doctype.doctype.doctype.START_WITH_LETTERS_PATTERN +# to allow table names like __Auth +TABLE_NAME_PATTERN = re.compile(r"^[\w -]*$", flags=re.ASCII) + class Base: terms = terms @@ -30,6 +35,7 @@ class Base: @staticmethod def DocType(table_name: str, *args, **kwargs) -> Table: + Base.validate_doctype(table_name) table_name = get_table_name(table_name) return Table(table_name, *args, **kwargs) @@ -45,6 +51,13 @@ class Base: table = cls.DocType(table) return super().update(table, *args, **kwargs) + @staticmethod + def validate_doctype(doctype) -> None: + from frappe import _, throw + + if not TABLE_NAME_PATTERN.match(doctype): + throw(_("Invalid DocType: {0}").format(doctype)) + class MariaDB(Base, MySQLQuery): Field = terms.Field