diff --git a/README.md b/README.md index 26ff1b132acf69818f111363889843ada27715b5..c53e60e1681ea70b4b739cb99752b7e6c2fc7f95 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,9 @@ Then copy your resulting wheel and install it via pip on the target system. emit a warning if the user forgot to * if page_size is default, it won't be written as part of the metadata * added support for per-series metadata +* following additions to `Database`: + * `delete_series` + * `delete_varlen_series` * following additions to `TimeSeries`: * added `append_padded` * added metadata support, `metadata` property and `set_metadata` call diff --git a/tempsdb/database.pxd b/tempsdb/database.pxd index c729f440103e73068026473157966243e6cce560..c3dda7aaa00501f921b46314bd3bfb7acbac4b5f 100644 --- a/tempsdb/database.pxd +++ b/tempsdb/database.pxd @@ -25,6 +25,8 @@ cdef class Database: int size_struct, unsigned long entries_per_chunk, int gzip_level=*) + cpdef int delete_series(self, str name) except -1 + cpdef int delete_varlen_series(self, str name) except -1 cpdef list get_open_series(self) cpdef list get_all_series(self) cpdef int close_all_open_series(self) except -1 diff --git a/tempsdb/database.pyx b/tempsdb/database.pyx index 2d54421135499288f5e527c468b0b7ccf245acdc..5ddaaf4cbb6f2edb73350f152ee3f3be7b1b3cef 100644 --- a/tempsdb/database.pyx +++ b/tempsdb/database.pyx @@ -1,10 +1,11 @@ import os +import shutil import threading import warnings from satella.coding import DictDeleter -from tempsdb.exceptions import DoesNotExist, AlreadyExists +from tempsdb.exceptions import DoesNotExist, AlreadyExists, StillOpen from .series cimport TimeSeries, create_series from .varlen cimport VarlenSeries, create_varlen_series @@ -53,6 +54,47 @@ cdef class Database: output.append(series) return output + cpdef int delete_series(self, str name) except -1: + """ + Deletes a constant-length time series + + :param name: name of series to delete + :raises ValueError: tried to delete "varlen" series + :raises StillOpen: series is open + """ + if name == 'varlen': + raise ValueError('tried to delete varlen series') + if not os.path.exists(os.path.join(self.path, name)): + raise DoesNotExist('series does not exist') + cdef TimeSeries series + with self.lock: + if name in self.open_series: + series = self.open_series[name] + if not series.closed: + raise StillOpen('series is open!') + shutil.rmtree(os.path.join(self.path, name)) + return 0 + + cpdef int delete_varlen_series(self, str name) except -1: + """ + Deletes a variable-length time series + + :param name: name of series to delete + :raises DoesNotExist: series does not exist + :raises StillOpen: series is open + """ + cdef str path = os.path.join(self.path, 'varlen', name) + if not os.path.exists(path): + raise DoesNotExist('series does not exist') + cdef VarlenSeries series + with self.lock: + if name in self.open_varlen_series: + series = self.open_varlen_series[name] + if not series.closed: + raise StillOpen('series is open!') + shutil.rmtree(path) + return 0 + cpdef TimeSeries get_series(self, name: str, bint use_descriptor_based_access = False): """ Load and return an existing series @@ -207,7 +249,9 @@ cdef class Database: bint use_descriptor_based_access=False, int gzip_level=0): """ - Create a new series + Create a new series. + + Note that series cannot be named "varlen" :param name: name of the series :param block_size: size of the data field @@ -217,11 +261,14 @@ cdef class Database: Default is False :param gzip_level: gzip compression level. Default is 0 which means "don't use gzip" :return: new series - :raises ValueError: block size was larger than page_size plus a timestamp + :raises ValueError: block size was larger than page_size plus a timestamp or series was named + "varlen" :raises AlreadyExists: series with given name already exists """ if block_size > page_size + 8: raise ValueError('Invalid block size, pick larger page') + if name == 'varlen': + raise ValueError('Series cannot be named varlen') if os.path.isdir(os.path.join(self.path, name)): raise AlreadyExists('Series already exists') if gzip_level: diff --git a/tests/test_database.py b/tests/test_database.py index 15cb59e8443512541303a3082ddf4c77048aa791..2d1686db09cc091e3f8134b0d10f9477b01e8e5c 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -1,6 +1,7 @@ import unittest from tempsdb.database import create_database +from tempsdb.exceptions import DoesNotExist class TestDatabase(unittest.TestCase): @@ -18,6 +19,22 @@ class TestDatabase(unittest.TestCase): self.assertEqual(ser.last_entry_ts, 20) ser.close() + self.db.delete_series('hello-world') + self.assertRaises(DoesNotExist, lambda: self.db.get_series('hello-world')) + + def test_add_varlen_series(self): + ser = self.db.create_varlen_series('hello-world', [10, 20], 1, 20) + ser.append(10, b'\x00') + ser.append(20, b'\x00\x00\x00') + ser.close() + + ser = self.db.get_varlen_series('hello-world') + self.assertEqual(ser.last_entry_ts, 20) + ser.close() + + self.db.delete_varlen_series('hello-world') + self.assertRaises(DoesNotExist, lambda: self.db.get_varlen_series('hello-world')) + @classmethod def tearDownClass(cls) -> None: cls.db.close()