Ensure all SQL functions are supported by the backend's

issue8942
review270751002
This commit is contained in:
Cédric Krier
2020-01-13 23:47:17 +01:00
parent e2d8b6b295
commit 606bdb7ce9
2 changed files with 106 additions and 4 deletions
+46 -4
View File
@@ -2,7 +2,9 @@
# this repository contains the full copyright notices and license terms.
import datetime
import logging
import math
import os
import random
import threading
import time
from decimal import Decimal
@@ -230,6 +232,10 @@ class SQLiteTrim(Trim):
return function + '(%s, %s)' % (
format(self.string), format(self.characters))
@property
def params(self):
return [self.string, self.characters]
def sign(value):
if value > 0:
@@ -256,6 +262,18 @@ def least(*args):
return None
def cbrt(value):
return math.pow(value, 1 / 3)
def div(a, b):
return a // b
def trunc(value, digits):
return math.trunc(value * 10 ** digits) / 10 ** digits
MAPPING = {
Extract: SQLiteExtract,
Position: SQLitePosition,
@@ -333,16 +351,40 @@ class Database(DatabaseInterface):
self._conn.create_function('extract', 2, SQLiteExtract.extract)
self._conn.create_function('date_trunc', 2, date_trunc)
self._conn.create_function('split_part', 3, split_part)
self._conn.create_function('position', 2, SQLitePosition.position)
self._conn.create_function('to_char', 2, to_char)
self._conn.create_function('overlay', 3, SQLiteOverlay.overlay)
self._conn.create_function('overlay', 4, SQLiteOverlay.overlay)
if sqlite.sqlite_version_info < (3, 3, 14):
self._conn.create_function('replace', 3, replace)
self._conn.create_function('now', 0, now)
self._conn.create_function('sign', 1, sign)
self._conn.create_function('greatest', -1, greatest)
self._conn.create_function('least', -1, least)
# Mathematical functions
self._conn.create_function('cbrt', 1, cbrt)
self._conn.create_function('ceil', 1, math.ceil)
self._conn.create_function('degrees', 1, math.degrees)
self._conn.create_function('div', 2, div)
self._conn.create_function('exp', 1, math.exp)
self._conn.create_function('floor', 1, math.floor)
self._conn.create_function('ln', 1, math.log)
self._conn.create_function('log', 1, math.log10)
self._conn.create_function('mod', 2, math.fmod)
self._conn.create_function('pi', 0, lambda: math.pi)
self._conn.create_function('power', 2, math.pow)
self._conn.create_function('radians', 1, math.radians)
self._conn.create_function('sign', 1, sign)
self._conn.create_function('sqrt', 1, math.sqrt)
self._conn.create_function('trunc', 1, math.trunc)
self._conn.create_function('trunc', 2, trunc)
# Random functions
self._conn.create_function('random', 0, random.random)
self._conn.create_function('setseed', 1, random.seed)
# String functions
self._conn.create_function('overlay', 3, SQLiteOverlay.overlay)
self._conn.create_function('overlay', 4, SQLiteOverlay.overlay)
self._conn.create_function('position', 2, SQLitePosition.position)
if (hasattr(self._conn, 'set_trace_callback')
and logger.isEnabledFor(logging.DEBUG)):
self._conn.set_trace_callback(logger.debug)
+60
View File
@@ -1,9 +1,11 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import datetime
import math
import unittest
from sql import Select
from sql import functions
from sql.functions import CurrentTimestamp, ToChar
from trytond.tests.test_tryton import activate_module, with_transaction
@@ -84,6 +86,64 @@ class BackendTestCase(unittest.TestCase):
self.assertEqual(text, today.strftime('%Y-%m-%d'))
@with_transaction()
def test_functions(self):
"Test functions"
cursor = Transaction().connection.cursor()
tests = [
(functions.Abs(-1), 1),
(functions.Cbrt(27), 3),
(functions.Ceil(-42.8), -42),
(functions.Degrees(0.5), 28.6478897565412),
(functions.Div(9, 4), 2),
(functions.Exp(1.), math.e),
(functions.Floor(-42.8), -43),
(functions.Ln(2.), 0.693147180559945),
(functions.Log(100.0), 2),
(functions.Mod(9, 4), 1),
(functions.Pi(), math.pi),
(functions.Power(9, 3), 729),
(functions.Radians(45.), math.pi / 4),
(functions.Round(42.4), 42),
(functions.Round(42.4382, 2), 42.44),
(functions.Sign(-8.4), -1),
(functions.Sqrt(2.), 1.4142135623731),
(functions.Trunc(42.8), 42),
(functions.Trunc(42.4348, 2), 42.43),
(functions.CharLength('jose'), 4),
(functions.Lower('TOM'), 'tom'),
(functions.Overlay('Txxxxas', 'hom', 2, 4), 'Thomas'),
(functions.Position('om', 'Thomas'), 3),
(functions.Substring('Thomas', 2, 3), 'hom'),
# (functions.Substring('Thomas', '...$'), 'mas'),
# (functions.Substring('Thomas', '%#"o_a#"_', '#'), 'oma'),
(functions.Trim('yxTomxx', 'BOTH', 'xyz'), 'Tom'),
(functions.Upper('tom'), 'TOM'),
]
for func, result in tests:
with self.subTest(func=str(func)):
cursor.execute(*Select([func]))
value, = cursor.fetchone()
if isinstance(result, str):
self.assertEqual(value, result)
else:
self.assertAlmostEqual(float(value), float(result))
@with_transaction()
def test_function_random(self):
"Test RANDOM function"
cursor = Transaction().connection.cursor()
cursor.execute(*Select([functions.Random()]))
value, = cursor.fetchone()
self.assertGreaterEqual(value, 0)
self.assertLessEqual(value, 1)
@with_transaction()
def test_function_setseed(self):
"Test SETSEED function"
cursor = Transaction().connection.cursor()
cursor.execute(*Select([functions.SetSeed(1)]))
def suite():
return unittest.TestLoader().loadTestsFromTestCase(BackendTestCase)