Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

1345 řádky
38 KiB

  1. # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import datetime
  4. import json
  5. import random
  6. import re
  7. import string
  8. import traceback
  9. from contextlib import contextmanager
  10. from time import time
  11. from pypika.terms import Criterion, NullValue
  12. import frappe
  13. import frappe.defaults
  14. import frappe.model.meta
  15. from frappe import _
  16. from frappe.database.utils import (
  17. EmptyQueryValues,
  18. FallBackDateTimeStr,
  19. LazyMogrify,
  20. Query,
  21. QueryValues,
  22. is_query_type,
  23. )
  24. from frappe.exceptions import DoesNotExistError, ImplicitCommitError
  25. from frappe.model.utils.link_count import flush_local_link_count
  26. from frappe.query_builder.functions import Count
  27. from frappe.utils import cast as cast_fieldtype
  28. from frappe.utils import get_datetime, get_table_name, getdate, now, sbool
  29. IFNULL_PATTERN = re.compile(r"ifnull\(", flags=re.IGNORECASE)
  30. INDEX_PATTERN = re.compile(r"\s*\([^)]+\)\s*")
  31. SINGLE_WORD_PATTERN = re.compile(r'([`"]?)(tab([A-Z]\w+))\1')
  32. MULTI_WORD_PATTERN = re.compile(r'([`"])(tab([A-Z]\w+)( [A-Z]\w+)+)\1')
  33. class Database:
  34. """
  35. Open a database connection with the given parmeters, if use_default is True, use the
  36. login details from `conf.py`. This is called by the request handler and is accessible using
  37. the `db` global variable. the `sql` method is also global to run queries
  38. """
  39. VARCHAR_LEN = 140
  40. MAX_COLUMN_LENGTH = 64
  41. OPTIONAL_COLUMNS = ["_user_tags", "_comments", "_assign", "_liked_by"]
  42. DEFAULT_SHORTCUTS = ["_Login", "__user", "_Full Name", "Today", "__today", "now", "Now"]
  43. STANDARD_VARCHAR_COLUMNS = ("name", "owner", "modified_by")
  44. DEFAULT_COLUMNS = ["name", "creation", "modified", "modified_by", "owner", "docstatus", "idx"]
  45. CHILD_TABLE_COLUMNS = ("parent", "parenttype", "parentfield")
  46. MAX_WRITES_PER_TRANSACTION = 200_000
  47. # NOTE:
  48. # FOR MARIADB - using no cache - as during backup, if the sequence was used in anyform,
  49. # it drops the cache and uses the next non cached value in setval query and
  50. # puts that in the backup file, which will start the counter
  51. # from that value when inserting any new record in the doctype.
  52. # By default the cache is 1000 which will mess up the sequence when
  53. # using the system after a restore.
  54. #
  55. # Another case could be if the cached values expire then also there is a chance of
  56. # the cache being skipped.
  57. #
  58. # FOR POSTGRES - The sequence cache for postgres is per connection.
  59. # Since we're opening and closing connections for every request this results in skipping the cache
  60. # to the next non-cached value hence not using cache in postgres.
  61. # ref: https://stackoverflow.com/questions/21356375/postgres-9-0-4-sequence-skipping-numbers
  62. SEQUENCE_CACHE = 0
  63. class InvalidColumnName(frappe.ValidationError):
  64. pass
  65. def __init__(
  66. self,
  67. host=None,
  68. user=None,
  69. password=None,
  70. ac_name=None,
  71. use_default=0,
  72. port=None,
  73. ):
  74. self.setup_type_map()
  75. self.host = host or frappe.conf.db_host or "127.0.0.1"
  76. self.port = port or frappe.conf.db_port or ""
  77. self.user = user or frappe.conf.db_name
  78. self.db_name = frappe.conf.db_name
  79. self._conn = None
  80. if ac_name:
  81. self.user = ac_name or frappe.conf.db_name
  82. if use_default:
  83. self.user = frappe.conf.db_name
  84. self.transaction_writes = 0
  85. self.auto_commit_on_many_writes = 0
  86. self.password = password or frappe.conf.db_password
  87. self.value_cache = {}
  88. # self.db_type: str
  89. # self.last_query (lazy) attribute of last sql query executed
  90. def setup_type_map(self):
  91. pass
  92. def connect(self):
  93. """Connects to a database as set in `site_config.json`."""
  94. self.cur_db_name = self.user
  95. self._conn = self.get_connection()
  96. self._cursor = self._conn.cursor()
  97. frappe.local.rollback_observers = []
  98. def use(self, db_name):
  99. """`USE` db_name."""
  100. self._conn.select_db(db_name)
  101. def get_connection(self):
  102. """Returns a Database connection object that conforms with https://peps.python.org/pep-0249/#connection-objects"""
  103. raise NotImplementedError
  104. def get_database_size(self):
  105. raise NotImplementedError
  106. def _transform_query(self, query: Query, values: QueryValues) -> tuple:
  107. return query, values
  108. def _transform_result(self, result: list[tuple]) -> list[tuple]:
  109. return result
  110. def sql(
  111. self,
  112. query: Query,
  113. values: QueryValues = EmptyQueryValues,
  114. as_dict=0,
  115. as_list=0,
  116. formatted=0,
  117. debug=0,
  118. ignore_ddl=0,
  119. as_utf8=0,
  120. auto_commit=0,
  121. update=None,
  122. explain=False,
  123. run=True,
  124. pluck=False,
  125. ):
  126. """Execute a SQL query and fetch all rows.
  127. :param query: SQL query.
  128. :param values: Tuple / List / Dict of values to be escaped and substituted in the query.
  129. :param as_dict: Return as a dictionary.
  130. :param as_list: Always return as a list.
  131. :param formatted: Format values like date etc.
  132. :param debug: Print query and `EXPLAIN` in debug log.
  133. :param ignore_ddl: Catch exception if table, column missing.
  134. :param as_utf8: Encode values as UTF 8.
  135. :param auto_commit: Commit after executing the query.
  136. :param update: Update this dict to all rows (if returned `as_dict`).
  137. :param run: Returns query without executing it if False.
  138. Examples:
  139. # return customer names as dicts
  140. frappe.db.sql("select name from tabCustomer", as_dict=True)
  141. # return names beginning with a
  142. frappe.db.sql("select name from tabCustomer where name like %s", "a%")
  143. # values as dict
  144. frappe.db.sql("select name from tabCustomer where name like %(name)s and owner=%(owner)s",
  145. {"name": "a%", "owner":"test@example.com"})
  146. """
  147. debug = debug or getattr(self, "debug", False)
  148. query = str(query)
  149. if not run:
  150. return query
  151. # remove whitespace / indentation from start and end of query
  152. query = query.strip()
  153. # replaces ifnull in query with coalesce
  154. query = IFNULL_PATTERN.sub("coalesce(", query)
  155. if not self._conn:
  156. self.connect()
  157. # in transaction validations
  158. self.check_transaction_status(query)
  159. self.clear_db_table_cache(query)
  160. if auto_commit:
  161. self.commit()
  162. if debug:
  163. time_start = time()
  164. if values == EmptyQueryValues:
  165. values = None
  166. elif not isinstance(values, (tuple, dict, list)):
  167. values = (values,)
  168. query, values = self._transform_query(query, values)
  169. try:
  170. self._cursor.execute(query, values)
  171. except Exception as e:
  172. if self.is_syntax_error(e):
  173. frappe.errprint(f"Syntax error in query:\n{query} {values}")
  174. elif self.is_deadlocked(e):
  175. raise frappe.QueryDeadlockError(e) from e
  176. elif self.is_timedout(e):
  177. raise frappe.QueryTimeoutError(e) from e
  178. elif self.is_read_only_mode_error(e):
  179. frappe.throw(
  180. _(
  181. "Site is running in read only mode, this action can not be performed right now. Please try again later."
  182. ),
  183. title=_("In Read Only Mode"),
  184. exc=frappe.InReadOnlyMode,
  185. )
  186. # TODO: added temporarily
  187. elif self.db_type == "postgres":
  188. traceback.print_stack()
  189. frappe.errprint(f"Error in query:\n{e}")
  190. raise
  191. elif isinstance(e, self.ProgrammingError):
  192. if frappe.conf.developer_mode:
  193. traceback.print_stack()
  194. frappe.errprint(f"Error in query:\n{query, values}")
  195. raise
  196. if not (
  197. ignore_ddl
  198. and (self.is_missing_column(e) or self.is_table_missing(e) or self.cant_drop_field_or_key(e))
  199. ):
  200. raise
  201. if debug:
  202. time_end = time()
  203. frappe.errprint(f"Execution time: {time_end - time_start:.2f} sec")
  204. self.log_query(query, values, debug, explain)
  205. if auto_commit:
  206. self.commit()
  207. if not self._cursor.description:
  208. return ()
  209. self.last_result = self._transform_result(self._cursor.fetchall())
  210. if pluck:
  211. return [r[0] for r in self.last_result]
  212. # scrub output if required
  213. if as_dict:
  214. ret = self.fetch_as_dict(formatted, as_utf8)
  215. if update:
  216. for r in ret:
  217. r.update(update)
  218. return ret
  219. elif as_list or as_utf8:
  220. return self.convert_to_lists(self.last_result, formatted, as_utf8)
  221. return self.last_result
  222. def _log_query(self, mogrified_query: str, debug: bool = False, explain: bool = False) -> None:
  223. """Takes the query and logs it to various interfaces according to the settings."""
  224. _query = None
  225. if frappe.conf.allow_tests and frappe.cache().get_value("flag_print_sql"):
  226. _query = _query or str(mogrified_query)
  227. print(_query)
  228. if debug:
  229. _query = _query or str(mogrified_query)
  230. if explain and is_query_type(_query, "select"):
  231. self.explain_query(_query)
  232. frappe.errprint(_query)
  233. if frappe.conf.logging == 2:
  234. _query = _query or str(mogrified_query)
  235. frappe.log(f"<<<< query\n{_query}\n>>>>")
  236. if frappe.flags.in_migrate:
  237. _query = _query or str(mogrified_query)
  238. self.log_touched_tables(_query)
  239. def log_query(
  240. self, query: str, values: QueryValues = None, debug: bool = False, explain: bool = False
  241. ) -> str:
  242. # TODO: Use mogrify until MariaDB Connector/C 1.1 is released and we can fetch something
  243. # like cursor._transformed_statement from the cursor object. We can also avoid setting
  244. # mogrified_query if we don't need to log it.
  245. mogrified_query = self.lazy_mogrify(query, values)
  246. self._log_query(mogrified_query, debug, explain)
  247. return mogrified_query
  248. def mogrify(self, query: Query, values: QueryValues):
  249. """build the query string with values"""
  250. if not values:
  251. return query
  252. try:
  253. return self._cursor.mogrify(query, values)
  254. except AttributeError:
  255. if isinstance(values, dict):
  256. return query % {k: frappe.db.escape(v) if isinstance(v, str) else v for k, v in values.items()}
  257. elif isinstance(values, (list, tuple)):
  258. return query % tuple(frappe.db.escape(v) if isinstance(v, str) else v for v in values)
  259. return query, values
  260. def lazy_mogrify(self, query: Query, values: QueryValues) -> LazyMogrify:
  261. """Wrap the object with str to generate mogrified query."""
  262. return LazyMogrify(query, values)
  263. def explain_query(self, query, values=None):
  264. """Print `EXPLAIN` in error log."""
  265. frappe.errprint("--- query explain ---")
  266. try:
  267. self._cursor.execute(f"EXPLAIN {query}", values)
  268. except Exception as e:
  269. frappe.errprint(f"error in query explain: {e}")
  270. else:
  271. frappe.errprint(json.dumps(self.fetch_as_dict(), indent=1))
  272. frappe.errprint("--- query explain end ---")
  273. def sql_list(self, query, values=(), debug=False, **kwargs):
  274. """Return data as list of single elements (first column).
  275. Example:
  276. # doctypes = ["DocType", "DocField", "User", ...]
  277. doctypes = frappe.db.sql_list("select name from DocType")
  278. """
  279. return self.sql(query, values, **kwargs, debug=debug, pluck=True)
  280. def sql_ddl(self, query, debug=False):
  281. """Commit and execute a query. DDL (Data Definition Language) queries that alter schema
  282. autocommit in MariaDB."""
  283. self.commit()
  284. self.sql(query, debug=debug)
  285. def check_transaction_status(self, query):
  286. """Raises exception if more than 20,000 `INSERT`, `UPDATE` queries are
  287. executed in one transaction. This is to ensure that writes are always flushed otherwise this
  288. could cause the system to hang."""
  289. self.check_implicit_commit(query)
  290. if query and is_query_type(query, ("commit", "rollback")):
  291. self.transaction_writes = 0
  292. if query[:6].lower() in ("update", "insert", "delete"):
  293. self.transaction_writes += 1
  294. if self.transaction_writes > self.MAX_WRITES_PER_TRANSACTION:
  295. if self.auto_commit_on_many_writes:
  296. self.commit()
  297. else:
  298. msg = "<br><br>" + _("Too many changes to database in single action.") + "<br>"
  299. msg += _("The changes have been reverted.") + "<br>"
  300. raise frappe.TooManyWritesError(msg)
  301. def check_implicit_commit(self, query):
  302. if (
  303. self.transaction_writes
  304. and query
  305. and is_query_type(query, ("start", "alter", "drop", "create", "begin", "truncate"))
  306. ):
  307. raise ImplicitCommitError("This statement can cause implicit commit")
  308. def fetch_as_dict(self, formatted=0, as_utf8=0) -> list[frappe._dict]:
  309. """Internal. Converts results to dict."""
  310. result = self.last_result
  311. ret = []
  312. if result:
  313. keys = [column[0] for column in self._cursor.description]
  314. for r in result:
  315. values = []
  316. for value in r:
  317. if as_utf8 and isinstance(value, str):
  318. value = value.encode("utf-8")
  319. values.append(value)
  320. ret.append(frappe._dict(zip(keys, values)))
  321. return ret
  322. @staticmethod
  323. def clear_db_table_cache(query):
  324. if query and is_query_type(query, ("drop", "create")):
  325. frappe.cache().delete_key("db_tables")
  326. @staticmethod
  327. def needs_formatting(result, formatted):
  328. """Returns true if the first row in the result has a Date, Datetime, Long Int."""
  329. if result and result[0]:
  330. for v in result[0]:
  331. if isinstance(v, (datetime.date, datetime.timedelta, datetime.datetime, int)):
  332. return True
  333. if formatted and isinstance(v, (int, float)):
  334. return True
  335. return False
  336. def get_description(self):
  337. """Returns result metadata."""
  338. return self._cursor.description
  339. @staticmethod
  340. def convert_to_lists(res, formatted=0, as_utf8=0):
  341. """Convert tuple output to lists (internal)."""
  342. nres = []
  343. for r in res:
  344. nr = []
  345. for val in r:
  346. if as_utf8 and isinstance(val, str):
  347. val = val.encode("utf-8")
  348. nr.append(val)
  349. nres.append(nr)
  350. return nres
  351. def get(self, doctype, filters=None, as_dict=True, cache=False):
  352. """Returns `get_value` with fieldname='*'"""
  353. return self.get_value(doctype, filters, "*", as_dict=as_dict, cache=cache)
  354. def get_value(
  355. self,
  356. doctype,
  357. filters=None,
  358. fieldname="name",
  359. ignore=None,
  360. as_dict=False,
  361. debug=False,
  362. order_by="KEEP_DEFAULT_ORDERING",
  363. cache=False,
  364. for_update=False,
  365. *,
  366. run=True,
  367. pluck=False,
  368. distinct=False,
  369. ):
  370. """Returns a document property or list of properties.
  371. :param doctype: DocType name.
  372. :param filters: Filters like `{"x":"y"}` or name of the document. `None` if Single DocType.
  373. :param fieldname: Column name.
  374. :param ignore: Don't raise exception if table, column is missing.
  375. :param as_dict: Return values as dict.
  376. :param debug: Print query in error log.
  377. :param order_by: Column to order by
  378. Example:
  379. # return first customer starting with a
  380. frappe.db.get_value("Customer", {"name": ("like a%")})
  381. # return last login of **User** `test@example.com`
  382. frappe.db.get_value("User", "test@example.com", "last_login")
  383. last_login, last_ip = frappe.db.get_value("User", "test@example.com",
  384. ["last_login", "last_ip"])
  385. # returns default date_format
  386. frappe.db.get_value("System Settings", None, "date_format")
  387. """
  388. result = self.get_values(
  389. doctype,
  390. filters,
  391. fieldname,
  392. ignore,
  393. as_dict,
  394. debug,
  395. order_by,
  396. cache=cache,
  397. for_update=for_update,
  398. run=run,
  399. pluck=pluck,
  400. distinct=distinct,
  401. limit=1,
  402. )
  403. if not run:
  404. return result
  405. if not result:
  406. return None
  407. row = result[0]
  408. if len(row) > 1 or as_dict:
  409. return row
  410. # single field is requested, send it without wrapping in containers
  411. return row[0]
  412. def get_values(
  413. self,
  414. doctype,
  415. filters=None,
  416. fieldname="name",
  417. ignore=None,
  418. as_dict=False,
  419. debug=False,
  420. order_by="KEEP_DEFAULT_ORDERING",
  421. update=None,
  422. cache=False,
  423. for_update=False,
  424. *,
  425. run=True,
  426. pluck=False,
  427. distinct=False,
  428. limit=None,
  429. ):
  430. """Returns multiple document properties.
  431. :param doctype: DocType name.
  432. :param filters: Filters like `{"x":"y"}` or name of the document.
  433. :param fieldname: Column name.
  434. :param ignore: Don't raise exception if table, column is missing.
  435. :param as_dict: Return values as dict.
  436. :param debug: Print query in error log.
  437. :param order_by: Column to order by,
  438. :param distinct: Get Distinct results.
  439. Example:
  440. # return first customer starting with a
  441. customers = frappe.db.get_values("Customer", {"name": ("like a%")})
  442. # return last login of **User** `test@example.com`
  443. user = frappe.db.get_values("User", "test@example.com", "*")[0]
  444. """
  445. out = None
  446. if cache and isinstance(filters, str) and (doctype, filters, fieldname) in self.value_cache:
  447. return self.value_cache[(doctype, filters, fieldname)]
  448. if distinct:
  449. order_by = None
  450. if isinstance(filters, list):
  451. out = self._get_value_for_many_names(
  452. doctype=doctype,
  453. names=filters,
  454. field=fieldname,
  455. order_by=order_by,
  456. debug=debug,
  457. run=run,
  458. pluck=pluck,
  459. distinct=distinct,
  460. limit=limit,
  461. as_dict=as_dict,
  462. )
  463. else:
  464. fields = fieldname
  465. if fieldname != "*":
  466. if isinstance(fieldname, str):
  467. fields = [fieldname]
  468. if (filters is not None) and (filters != doctype or doctype == "DocType"):
  469. try:
  470. if order_by:
  471. order_by = "modified" if order_by == "KEEP_DEFAULT_ORDERING" else order_by
  472. out = self._get_values_from_table(
  473. fields=fields,
  474. filters=filters,
  475. doctype=doctype,
  476. as_dict=as_dict,
  477. debug=debug,
  478. order_by=order_by,
  479. update=update,
  480. for_update=for_update,
  481. run=run,
  482. pluck=pluck,
  483. distinct=distinct,
  484. limit=limit,
  485. )
  486. except Exception as e:
  487. if ignore and (frappe.db.is_missing_column(e) or frappe.db.is_table_missing(e)):
  488. # table or column not found, return None
  489. out = None
  490. elif (not ignore) and frappe.db.is_table_missing(e):
  491. # table not found, look in singles
  492. out = self.get_values_from_single(
  493. fields, filters, doctype, as_dict, debug, update, run=run, distinct=distinct
  494. )
  495. else:
  496. raise
  497. else:
  498. out = self.get_values_from_single(
  499. fields, filters, doctype, as_dict, debug, update, run=run, pluck=pluck, distinct=distinct
  500. )
  501. if cache and isinstance(filters, str):
  502. self.value_cache[(doctype, filters, fieldname)] = out
  503. return out
  504. def get_values_from_single(
  505. self,
  506. fields,
  507. filters,
  508. doctype,
  509. as_dict=False,
  510. debug=False,
  511. update=None,
  512. *,
  513. run=True,
  514. pluck=False,
  515. distinct=False,
  516. ):
  517. """Get values from `tabSingles` (Single DocTypes) (internal).
  518. :param fields: List of fields,
  519. :param filters: Filters (dict).
  520. :param doctype: DocType name.
  521. """
  522. if fields == "*" or isinstance(filters, dict):
  523. # check if single doc matches with filters
  524. values = self.get_singles_dict(doctype)
  525. if isinstance(filters, dict):
  526. for key, value in filters.items():
  527. if values.get(key) != value:
  528. return []
  529. if as_dict:
  530. return values and [values] or []
  531. if isinstance(fields, list):
  532. return [map(values.get, fields)]
  533. else:
  534. r = frappe.qb.engine.get_query(
  535. "Singles",
  536. filters={"field": ("in", tuple(fields)), "doctype": doctype},
  537. fields=["field", "value"],
  538. distinct=distinct,
  539. ).run(pluck=pluck, debug=debug, as_dict=False)
  540. if not run:
  541. return r
  542. if as_dict:
  543. if r:
  544. r = frappe._dict(r)
  545. if update:
  546. r.update(update)
  547. return [r]
  548. else:
  549. return []
  550. else:
  551. return r and [[i[1] for i in r]] or []
  552. def get_singles_dict(self, doctype, debug=False, *, for_update=False, cast=False):
  553. """Get Single DocType as dict.
  554. :param doctype: DocType of the single object whose value is requested
  555. :param debug: Execute query in debug mode - print to STDOUT
  556. :param for_update: Take `FOR UPDATE` lock on the records
  557. :param cast: Cast values to Python data types based on field type
  558. Example:
  559. # Get coulmn and value of the single doctype Accounts Settings
  560. account_settings = frappe.db.get_singles_dict("Accounts Settings")
  561. """
  562. queried_result = frappe.qb.engine.get_query(
  563. "Singles",
  564. filters={"doctype": doctype},
  565. fields=["field", "value"],
  566. for_update=for_update,
  567. ).run(debug=debug)
  568. if not cast:
  569. return frappe._dict(queried_result)
  570. try:
  571. meta = frappe.get_meta(doctype)
  572. except DoesNotExistError:
  573. return frappe._dict(queried_result)
  574. return_value = frappe._dict()
  575. for fieldname, value in queried_result:
  576. if df := meta.get_field(fieldname):
  577. casted_value = cast_fieldtype(df.fieldtype, value)
  578. else:
  579. casted_value = value
  580. return_value[fieldname] = casted_value
  581. return return_value
  582. @staticmethod
  583. def get_all(*args, **kwargs):
  584. return frappe.get_all(*args, **kwargs)
  585. @staticmethod
  586. def get_list(*args, **kwargs):
  587. return frappe.get_list(*args, **kwargs)
  588. def set_single_value(
  589. self,
  590. doctype: str,
  591. fieldname: str | dict,
  592. value: str | int | None = None,
  593. *args,
  594. **kwargs,
  595. ):
  596. """Set field value of Single DocType.
  597. :param doctype: DocType of the single object
  598. :param fieldname: `fieldname` of the property
  599. :param value: `value` of the property
  600. Example:
  601. # Update the `deny_multiple_sessions` field in System Settings DocType.
  602. company = frappe.db.set_single_value("System Settings", "deny_multiple_sessions", True)
  603. """
  604. return self.set_value(doctype, doctype, fieldname, value, *args, **kwargs)
  605. def get_single_value(self, doctype, fieldname, cache=True):
  606. """Get property of Single DocType. Cache locally by default
  607. :param doctype: DocType of the single object whose value is requested
  608. :param fieldname: `fieldname` of the property whose value is requested
  609. Example:
  610. # Get the default value of the company from the Global Defaults doctype.
  611. company = frappe.db.get_single_value('Global Defaults', 'default_company')
  612. """
  613. if doctype not in self.value_cache:
  614. self.value_cache[doctype] = {}
  615. if cache and fieldname in self.value_cache[doctype]:
  616. return self.value_cache[doctype][fieldname]
  617. val = frappe.qb.engine.get_query(
  618. table="Singles",
  619. filters={"doctype": doctype, "field": fieldname},
  620. fields="value",
  621. ).run()
  622. val = val[0][0] if val else None
  623. df = frappe.get_meta(doctype).get_field(fieldname)
  624. if not df:
  625. frappe.throw(
  626. _("Invalid field name: {0}").format(frappe.bold(fieldname)), self.InvalidColumnName
  627. )
  628. val = cast_fieldtype(df.fieldtype, val)
  629. self.value_cache[doctype][fieldname] = val
  630. return val
  631. def get_singles_value(self, *args, **kwargs):
  632. """Alias for get_single_value"""
  633. return self.get_single_value(*args, **kwargs)
  634. def _get_values_from_table(
  635. self,
  636. fields,
  637. filters,
  638. doctype,
  639. as_dict,
  640. *,
  641. debug=False,
  642. order_by=None,
  643. update=None,
  644. for_update=False,
  645. run=True,
  646. pluck=False,
  647. distinct=False,
  648. limit=None,
  649. ):
  650. field_objects = []
  651. query = frappe.qb.engine.get_query(
  652. table=doctype,
  653. filters=filters,
  654. orderby=order_by,
  655. for_update=for_update,
  656. field_objects=field_objects,
  657. fields=fields,
  658. distinct=distinct,
  659. limit=limit,
  660. )
  661. if fields == "*" and not isinstance(fields, (list, tuple)) and not isinstance(fields, Criterion):
  662. as_dict = True
  663. return self.sql(query, as_dict=as_dict, debug=debug, update=update, run=run, pluck=pluck)
  664. def _get_value_for_many_names(
  665. self,
  666. doctype,
  667. names,
  668. field,
  669. order_by,
  670. *,
  671. debug=False,
  672. run=True,
  673. pluck=False,
  674. distinct=False,
  675. limit=None,
  676. as_dict=False,
  677. ):
  678. if names := list(filter(None, names)):
  679. return self.get_all(
  680. doctype,
  681. fields=field,
  682. filters=names,
  683. order_by=order_by,
  684. pluck=pluck,
  685. debug=debug,
  686. as_list=not as_dict,
  687. run=run,
  688. distinct=distinct,
  689. limit_page_length=limit,
  690. )
  691. return {}
  692. def update(self, *args, **kwargs):
  693. """Update multiple values. Alias for `set_value`."""
  694. return self.set_value(*args, **kwargs)
  695. def set_value(
  696. self,
  697. dt,
  698. dn,
  699. field,
  700. val=None,
  701. modified=None,
  702. modified_by=None,
  703. update_modified=True,
  704. debug=False,
  705. for_update=True,
  706. ):
  707. """Set a single value in the database, do not call the ORM triggers
  708. but update the modified timestamp (unless specified not to).
  709. **Warning:** this function will not call Document events and should be avoided in normal cases.
  710. :param dt: DocType name.
  711. :param dn: Document name.
  712. :param field: Property / field name or dictionary of values to be updated
  713. :param value: Value to be updated.
  714. :param modified: Use this as the `modified` timestamp.
  715. :param modified_by: Set this user as `modified_by`.
  716. :param update_modified: default True. Set as false, if you don't want to update the timestamp.
  717. :param debug: Print the query in the developer / js console.
  718. :param for_update: [DEPRECATED] This function now performs updates in single query, locking is not required.
  719. """
  720. is_single_doctype = not (dn and dt != dn)
  721. to_update = field if isinstance(field, dict) else {field: val}
  722. if update_modified:
  723. modified = modified or now()
  724. modified_by = modified_by or frappe.session.user
  725. to_update.update({"modified": modified, "modified_by": modified_by})
  726. if is_single_doctype:
  727. frappe.db.delete(
  728. "Singles", filters={"field": ("in", tuple(to_update)), "doctype": dt}, debug=debug
  729. )
  730. singles_data = ((dt, key, sbool(value)) for key, value in to_update.items())
  731. query = (
  732. frappe.qb.into("Singles").columns("doctype", "field", "value").insert(*singles_data)
  733. ).run(debug=debug)
  734. frappe.clear_document_cache(dt, dt)
  735. else:
  736. query = frappe.qb.engine.build_conditions(table=dt, filters=dn, update=True)
  737. if isinstance(dn, str):
  738. frappe.clear_document_cache(dt, dn)
  739. else:
  740. # TODO: Fix this; doesn't work rn - gavin@frappe.io
  741. # frappe.cache().hdel_keys(dt, "document_cache")
  742. # Workaround: clear all document caches
  743. frappe.cache().delete_value("document_cache")
  744. for column, value in to_update.items():
  745. query = query.set(column, value)
  746. query.run(debug=debug)
  747. if dt in self.value_cache:
  748. del self.value_cache[dt]
  749. @staticmethod
  750. def set(doc, field, val):
  751. """Set value in document. **Avoid**"""
  752. doc.db_set(field, val)
  753. def touch(self, doctype, docname):
  754. """Update the modified timestamp of this document."""
  755. modified = now()
  756. DocType = frappe.qb.DocType(doctype)
  757. frappe.qb.update(DocType).set(DocType.modified, modified).where(DocType.name == docname).run()
  758. return modified
  759. @staticmethod
  760. def set_temp(value):
  761. """Set a temperory value and return a key."""
  762. key = frappe.generate_hash()
  763. frappe.cache().hset("temp", key, value)
  764. return key
  765. @staticmethod
  766. def get_temp(key):
  767. """Return the temperory value and delete it."""
  768. return frappe.cache().hget("temp", key)
  769. def set_global(self, key, val, user="__global"):
  770. """Save a global key value. Global values will be automatically set if they match fieldname."""
  771. self.set_default(key, val, user)
  772. def get_global(self, key, user="__global"):
  773. """Returns a global key value."""
  774. return self.get_default(key, user)
  775. def get_default(self, key, parent="__default"):
  776. """Returns default value as a list if multiple or single"""
  777. d = self.get_defaults(key, parent)
  778. return isinstance(d, list) and d[0] or d
  779. @staticmethod
  780. def set_default(key, val, parent="__default", parenttype=None):
  781. """Sets a global / user default value."""
  782. frappe.defaults.set_default(key, val, parent, parenttype)
  783. @staticmethod
  784. def add_default(key, val, parent="__default", parenttype=None):
  785. """Append a default value for a key, there can be multiple default values for a particular key."""
  786. frappe.defaults.add_default(key, val, parent, parenttype)
  787. @staticmethod
  788. def get_defaults(key=None, parent="__default"):
  789. """Get all defaults"""
  790. defaults = frappe.defaults.get_defaults_for(parent)
  791. if not key:
  792. return defaults
  793. if key in defaults:
  794. return defaults[key]
  795. return defaults.get(frappe.scrub(key))
  796. def begin(self, *, read_only=False):
  797. read_only = read_only or frappe.flags.read_only
  798. mode = "READ ONLY" if read_only else ""
  799. self.sql(f"START TRANSACTION {mode}")
  800. def commit(self):
  801. """Commit current transaction. Calls SQL `COMMIT`."""
  802. for method in frappe.local.before_commit:
  803. frappe.call(method[0], *(method[1] or []), **(method[2] or {}))
  804. self.sql("commit")
  805. self.begin() # explicitly start a new transaction
  806. frappe.local.rollback_observers = []
  807. self.flush_realtime_log()
  808. enqueue_jobs_after_commit()
  809. flush_local_link_count()
  810. def add_before_commit(self, method, args=None, kwargs=None):
  811. frappe.local.before_commit.append([method, args, kwargs])
  812. @staticmethod
  813. def flush_realtime_log():
  814. for args in frappe.local.realtime_log:
  815. frappe.realtime.emit_via_redis(*args)
  816. frappe.local.realtime_log = []
  817. def savepoint(self, save_point):
  818. """Savepoints work as a nested transaction.
  819. Changes can be undone to a save point by doing frappe.db.rollback(save_point)
  820. Note: rollback watchers can not work with save points.
  821. so only changes to database are undone when rolling back to a savepoint.
  822. Avoid using savepoints when writing to filesystem."""
  823. self.sql(f"savepoint {save_point}")
  824. def release_savepoint(self, save_point):
  825. self.sql(f"release savepoint {save_point}")
  826. def rollback(self, *, save_point=None):
  827. """`ROLLBACK` current transaction. Optionally rollback to a known save_point."""
  828. if save_point:
  829. self.sql(f"rollback to savepoint {save_point}")
  830. else:
  831. self.sql("rollback")
  832. self.begin()
  833. for obj in dict.fromkeys(frappe.local.rollback_observers):
  834. if hasattr(obj, "on_rollback"):
  835. obj.on_rollback()
  836. frappe.local.rollback_observers = []
  837. def field_exists(self, dt, fn):
  838. """Return true of field exists."""
  839. return self.exists("DocField", {"fieldname": fn, "parent": dt})
  840. def table_exists(self, doctype, cached=True):
  841. """Returns True if table for given doctype exists."""
  842. return f"tab{doctype}" in self.get_tables(cached=cached)
  843. def has_table(self, doctype):
  844. return self.table_exists(doctype)
  845. def get_tables(self, cached=True):
  846. raise NotImplementedError
  847. def a_row_exists(self, doctype):
  848. """Returns True if atleast one row exists."""
  849. return frappe.get_all(doctype, limit=1, order_by=None, as_list=True)
  850. def exists(self, dt, dn=None, cache=False):
  851. """Return the document name of a matching document, or None.
  852. Note: `cache` only works if `dt` and `dn` are of type `str`.
  853. ## Examples
  854. Pass doctype and docname (only in this case we can cache the result)
  855. ```
  856. exists("User", "jane@example.org", cache=True)
  857. ```
  858. Pass a dict of filters including the `"doctype"` key:
  859. ```
  860. exists({"doctype": "User", "full_name": "Jane Doe"})
  861. ```
  862. Pass the doctype and a dict of filters:
  863. ```
  864. exists("User", {"full_name": "Jane Doe"})
  865. ```
  866. """
  867. if dt != "DocType" and dt == dn:
  868. # single always exists (!)
  869. return dn
  870. if isinstance(dt, dict):
  871. dt = dt.copy() # don't modify the original dict
  872. dt, dn = dt.pop("doctype"), dt
  873. return self.get_value(dt, dn, ignore=True, cache=cache)
  874. def count(self, dt, filters=None, debug=False, cache=False, distinct: bool = True):
  875. """Returns `COUNT(*)` for given DocType and filters."""
  876. if cache and not filters:
  877. cache_count = frappe.cache().get_value(f"doctype:count:{dt}")
  878. if cache_count is not None:
  879. return cache_count
  880. query = frappe.qb.engine.get_query(
  881. table=dt, filters=filters, fields=Count("*"), distinct=distinct
  882. )
  883. count = self.sql(query, debug=debug)[0][0]
  884. if not filters and cache:
  885. frappe.cache().set_value(f"doctype:count:{dt}", count, expires_in_sec=86400)
  886. return count
  887. @staticmethod
  888. def format_date(date):
  889. return getdate(date).strftime("%Y-%m-%d")
  890. @staticmethod
  891. def format_datetime(datetime):
  892. if not datetime:
  893. return FallBackDateTimeStr
  894. if isinstance(datetime, str):
  895. if ":" not in datetime:
  896. datetime = datetime + " 00:00:00.000000"
  897. else:
  898. datetime = datetime.strftime("%Y-%m-%d %H:%M:%S.%f")
  899. return datetime
  900. def get_creation_count(self, doctype, minutes):
  901. """Get count of records created in the last x minutes"""
  902. from dateutil.relativedelta import relativedelta
  903. from frappe.utils import now_datetime
  904. Table = frappe.qb.DocType(doctype)
  905. return (
  906. frappe.qb.from_(Table)
  907. .select(Count(Table.name))
  908. .where(Table.creation >= now_datetime() - relativedelta(minutes=minutes))
  909. .run()[0][0]
  910. )
  911. def get_db_table_columns(self, table) -> list[str]:
  912. """Returns list of column names from given table."""
  913. columns = frappe.cache().hget("table_columns", table)
  914. if columns is None:
  915. information_schema = frappe.qb.Schema("information_schema")
  916. columns = (
  917. frappe.qb.from_(information_schema.columns)
  918. .select(information_schema.columns.column_name)
  919. .where(information_schema.columns.table_name == table)
  920. .run(pluck=True)
  921. )
  922. if columns:
  923. frappe.cache().hset("table_columns", table, columns)
  924. return columns
  925. def get_table_columns(self, doctype):
  926. """Returns list of column names from given doctype."""
  927. columns = self.get_db_table_columns("tab" + doctype)
  928. if not columns:
  929. raise self.TableMissingError("DocType", doctype)
  930. return columns
  931. def has_column(self, doctype, column):
  932. """Returns True if column exists in database."""
  933. return column in self.get_table_columns(doctype)
  934. def get_column_type(self, doctype, column):
  935. """Returns column type from database."""
  936. information_schema = frappe.qb.Schema("information_schema")
  937. table = get_table_name(doctype)
  938. return (
  939. frappe.qb.from_(information_schema.columns)
  940. .select(information_schema.columns.column_type)
  941. .where(
  942. (information_schema.columns.table_name == table)
  943. & (information_schema.columns.column_name == column)
  944. )
  945. .run(pluck=True)[0]
  946. )
  947. def has_index(self, table_name, index_name):
  948. raise NotImplementedError
  949. def add_index(self, doctype, fields, index_name=None):
  950. raise NotImplementedError
  951. def add_unique(self, doctype, fields, constraint_name=None):
  952. raise NotImplementedError
  953. @staticmethod
  954. def get_index_name(fields):
  955. index_name = "_".join(fields) + "_index"
  956. # remove index length if present e.g. (10) from index name
  957. return INDEX_PATTERN.sub(r"", index_name)
  958. def get_system_setting(self, key):
  959. return frappe.get_system_settings(key)
  960. def close(self):
  961. """Close database connection."""
  962. if self._conn:
  963. self._conn.close()
  964. self._cursor = None
  965. self._conn = None
  966. @staticmethod
  967. def escape(s, percent=True):
  968. """Excape quotes and percent in given string."""
  969. # implemented in specific class
  970. raise NotImplementedError
  971. @staticmethod
  972. def is_column_missing(e):
  973. return frappe.db.is_missing_column(e)
  974. def get_descendants(self, doctype, name):
  975. """Return descendants of the group node in tree"""
  976. from frappe.utils.nestedset import get_descendants_of
  977. try:
  978. return get_descendants_of(doctype, name, ignore_permissions=True)
  979. except Exception:
  980. # Can only happen if document doesn't exists - kept for backward compatibility
  981. return []
  982. def is_missing_table_or_column(self, e):
  983. return self.is_missing_column(e) or self.is_table_missing(e)
  984. def multisql(self, sql_dict, values=(), **kwargs):
  985. current_dialect = self.db_type or "mariadb"
  986. query = sql_dict.get(current_dialect)
  987. return self.sql(query, values, **kwargs)
  988. def delete(self, doctype: str, filters: dict | list = None, debug=False, **kwargs):
  989. """Delete rows from a table in site which match the passed filters. This
  990. does trigger DocType hooks. Simply runs a DELETE query in the database.
  991. Doctype name can be passed directly, it will be pre-pended with `tab`.
  992. """
  993. filters = filters or kwargs.get("conditions")
  994. query = frappe.qb.engine.build_conditions(table=doctype, filters=filters).delete()
  995. if "debug" not in kwargs:
  996. kwargs["debug"] = debug
  997. return query.run(**kwargs)
  998. def truncate(self, doctype: str):
  999. """Truncate a table in the database. This runs a DDL command `TRUNCATE TABLE`.
  1000. This cannot be rolled back.
  1001. Doctype name can be passed directly, it will be pre-pended with `tab`.
  1002. """
  1003. return self.sql_ddl(f"truncate `{get_table_name(doctype)}`")
  1004. def clear_table(self, doctype):
  1005. return self.truncate(doctype)
  1006. def get_last_created(self, doctype):
  1007. last_record = self.get_all(doctype, ("creation"), limit=1, order_by="creation desc")
  1008. if last_record:
  1009. return get_datetime(last_record[0].creation)
  1010. else:
  1011. return None
  1012. def log_touched_tables(self, query):
  1013. if is_query_type(query, ("insert", "delete", "update", "alter", "drop", "rename")):
  1014. # single_word_regex is designed to match following patterns
  1015. # `tabXxx`, tabXxx and "tabXxx"
  1016. # multi_word_regex is designed to match following patterns
  1017. # `tabXxx Xxx` and "tabXxx Xxx"
  1018. # ([`"]?) Captures " or ` at the begining of the table name (if provided)
  1019. # \1 matches the first captured group (quote character) at the end of the table name
  1020. # multi word table name must have surrounding quotes.
  1021. # (tab([A-Z]\w+)( [A-Z]\w+)*) Captures table names that start with "tab"
  1022. # and are continued with multiple words that start with a captital letter
  1023. # e.g. 'tabXxx' or 'tabXxx Xxx' or 'tabXxx Xxx Xxx' and so on
  1024. tables = []
  1025. for regex in (SINGLE_WORD_PATTERN, MULTI_WORD_PATTERN):
  1026. tables += [groups[1] for groups in regex.findall(query)]
  1027. if frappe.flags.touched_tables is None:
  1028. frappe.flags.touched_tables = set()
  1029. frappe.flags.touched_tables.update(tables)
  1030. def bulk_insert(self, doctype, fields, values, ignore_duplicates=False, *, chunk_size=10_000):
  1031. """
  1032. Insert multiple records at a time
  1033. :param doctype: Doctype name
  1034. :param fields: list of fields
  1035. :params values: list of list of values
  1036. """
  1037. values = list(values)
  1038. table = frappe.qb.DocType(doctype)
  1039. for start_index in range(0, len(values), chunk_size):
  1040. query = frappe.qb.into(table)
  1041. if ignore_duplicates:
  1042. # Pypika does not have same api for ignoring duplicates
  1043. if self.db_type == "mariadb":
  1044. query = query.ignore()
  1045. elif self.db_type == "postgres":
  1046. query = query.on_conflict().do_nothing()
  1047. values_to_insert = values[start_index : start_index + chunk_size]
  1048. query.columns(fields).insert(*values_to_insert).run()
  1049. def create_sequence(self, *args, **kwargs):
  1050. from frappe.database.sequence import create_sequence
  1051. return create_sequence(*args, **kwargs)
  1052. def set_next_sequence_val(self, *args, **kwargs):
  1053. from frappe.database.sequence import set_next_val
  1054. set_next_val(*args, **kwargs)
  1055. def get_next_sequence_val(self, *args, **kwargs):
  1056. from frappe.database.sequence import get_next_val
  1057. return get_next_val(*args, **kwargs)
  1058. def enqueue_jobs_after_commit():
  1059. from frappe.utils.background_jobs import (
  1060. RQ_JOB_FAILURE_TTL,
  1061. RQ_RESULTS_TTL,
  1062. execute_job,
  1063. get_queue,
  1064. )
  1065. if frappe.flags.enqueue_after_commit and len(frappe.flags.enqueue_after_commit) > 0:
  1066. for job in frappe.flags.enqueue_after_commit:
  1067. q = get_queue(job.get("queue"), is_async=job.get("is_async"))
  1068. q.enqueue_call(
  1069. execute_job,
  1070. timeout=job.get("timeout"),
  1071. kwargs=job.get("queue_args"),
  1072. failure_ttl=RQ_JOB_FAILURE_TTL,
  1073. result_ttl=RQ_RESULTS_TTL,
  1074. )
  1075. frappe.flags.enqueue_after_commit = []
  1076. @contextmanager
  1077. def savepoint(catch: type | tuple[type, ...] = Exception):
  1078. """Wrapper for wrapping blocks of DB operations in a savepoint.
  1079. as contextmanager:
  1080. for doc in docs:
  1081. with savepoint(catch=DuplicateError):
  1082. doc.insert()
  1083. as decorator (wraps FULL function call):
  1084. @savepoint(catch=DuplicateError)
  1085. def process_doc(doc):
  1086. doc.insert()
  1087. """
  1088. try:
  1089. savepoint = "".join(random.sample(string.ascii_lowercase, 10))
  1090. frappe.db.savepoint(savepoint)
  1091. yield # control back to calling function
  1092. except catch:
  1093. frappe.db.rollback(save_point=savepoint)
  1094. else:
  1095. frappe.db.release_savepoint(savepoint)