vastdb 0.0.5.3__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. vast_flatbuf/tabular/GetTableStatsResponse.py +45 -1
  2. vast_flatbuf/tabular/VipRange.py +56 -0
  3. vastdb/__init__.py +7 -0
  4. vastdb/bench/test_perf.py +29 -0
  5. vastdb/bucket.py +85 -0
  6. vastdb/{tests/conftest.py → conftest.py} +29 -14
  7. vastdb/errors.py +175 -0
  8. vastdb/{api.py → internal_commands.py} +373 -875
  9. vastdb/schema.py +85 -0
  10. vastdb/session.py +47 -0
  11. vastdb/table.py +483 -0
  12. vastdb/tests/test_imports.py +123 -0
  13. vastdb/tests/test_nested.py +28 -0
  14. vastdb/tests/test_projections.py +42 -0
  15. vastdb/tests/test_sanity.py +34 -15
  16. vastdb/tests/test_schemas.py +30 -6
  17. vastdb/tests/test_tables.py +628 -13
  18. vastdb/tests/util.py +18 -0
  19. vastdb/transaction.py +54 -0
  20. vastdb/util.py +11 -10
  21. vastdb-0.1.1.dist-info/METADATA +38 -0
  22. {vastdb-0.0.5.3.dist-info → vastdb-0.1.1.dist-info}/RECORD +26 -31
  23. vast_protobuf/substrait/__init__.py +0 -0
  24. vast_protobuf/substrait/algebra_pb2.py +0 -1344
  25. vast_protobuf/substrait/capabilities_pb2.py +0 -46
  26. vast_protobuf/substrait/ddl_pb2.py +0 -57
  27. vast_protobuf/substrait/extended_expression_pb2.py +0 -49
  28. vast_protobuf/substrait/extensions/__init__.py +0 -0
  29. vast_protobuf/substrait/extensions/extensions_pb2.py +0 -89
  30. vast_protobuf/substrait/function_pb2.py +0 -168
  31. vast_protobuf/substrait/parameterized_types_pb2.py +0 -181
  32. vast_protobuf/substrait/plan_pb2.py +0 -67
  33. vast_protobuf/substrait/type_expressions_pb2.py +0 -198
  34. vast_protobuf/substrait/type_pb2.py +0 -350
  35. vast_protobuf/tabular/__init__.py +0 -0
  36. vast_protobuf/tabular/rpc_pb2.py +0 -344
  37. vastdb/bench_scan.py +0 -45
  38. vastdb/tests/test_create_table_from_parquets.py +0 -50
  39. vastdb/v2.py +0 -360
  40. vastdb-0.0.5.3.dist-info/METADATA +0 -47
  41. {vast_protobuf → vastdb/bench}/__init__.py +0 -0
  42. {vastdb-0.0.5.3.dist-info → vastdb-0.1.1.dist-info}/LICENSE +0 -0
  43. {vastdb-0.0.5.3.dist-info → vastdb-0.1.1.dist-info}/WHEEL +0 -0
  44. {vastdb-0.0.5.3.dist-info → vastdb-0.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,123 @@
1
+ import logging
2
+ from tempfile import NamedTemporaryFile
3
+
4
+ import pyarrow as pa
5
+ import pyarrow.parquet as pq
6
+ import pytest
7
+
8
+ from vastdb import util
9
+ from vastdb.errors import ImportFilesError, InvalidArgument
10
+
11
+ log = logging.getLogger(__name__)
12
+
13
+
14
+ def test_parallel_imports(session, clean_bucket_name, s3):
15
+ num_rows = 1000
16
+ num_files = 53
17
+ ds = {'num': [i for i in range(num_rows)]}
18
+ files = []
19
+ table = pa.Table.from_pydict(ds)
20
+ with NamedTemporaryFile() as f:
21
+ pq.write_table(table, f.name)
22
+ s3.put_object(Bucket=clean_bucket_name, Key='prq0', Body=f)
23
+ files.append(f'/{clean_bucket_name}/prq0')
24
+
25
+ for i in range(1, num_files):
26
+ copy_source = {
27
+ 'Bucket': clean_bucket_name,
28
+ 'Key': 'prq0'
29
+ }
30
+ s3.copy(copy_source, clean_bucket_name, f'prq{i}')
31
+ files.append(f'/{clean_bucket_name}/prq{i}')
32
+
33
+ with session.transaction() as tx:
34
+ b = tx.bucket(clean_bucket_name)
35
+ s = b.create_schema('s1')
36
+ t = s.create_table('t1', pa.schema([('num', pa.int64())]))
37
+ log.info("Starting import of %d files", num_files)
38
+ t.import_files(files)
39
+ arrow_table = pa.Table.from_batches(t.select(columns=['num']))
40
+ assert arrow_table.num_rows == num_rows * num_files
41
+ arrow_table = pa.Table.from_batches(t.select(columns=['num'], predicate=t['num'] == 100))
42
+ assert arrow_table.num_rows == num_files
43
+
44
+
45
+ def test_create_table_from_files(session, clean_bucket_name, s3):
46
+ datasets = [
47
+ {'num': [0],
48
+ 'varch': ['z']},
49
+ {'num': [1, 2, 3, 4, 5],
50
+ 'varch': ['a', 'b', 'c', 'd', 'e']},
51
+ {'num': [1, 2, 3, 4, 5],
52
+ 'bool': [True, False, None, None, False],
53
+ 'varch': ['a', 'b', 'c', 'd', 'e']},
54
+ {'num': [1, 2],
55
+ 'bool': [True, True]},
56
+ {'varch': ['a', 'b', 'c'],
57
+ 'mismatch': [1, 2, 3]}
58
+ ]
59
+ for i, ds in enumerate(datasets):
60
+ table = pa.Table.from_pydict(ds)
61
+ with NamedTemporaryFile() as f:
62
+ pq.write_table(table, f.name)
63
+ s3.put_object(Bucket=clean_bucket_name, Key=f'prq{i}', Body=f)
64
+
65
+ same_schema_files = [f'/{clean_bucket_name}/prq{i}' for i in range(2)]
66
+ contained_schema_files = [f'/{clean_bucket_name}/prq{i}' for i in range(4)]
67
+ different_schema_files = [f'/{clean_bucket_name}/prq{i}' for i in range(5)]
68
+
69
+ with session.transaction() as tx:
70
+ b = tx.bucket(clean_bucket_name)
71
+ s = b.create_schema('s1')
72
+ t = util.create_table_from_files(s, 't1', contained_schema_files)
73
+ assert len(t.arrow_schema) == 3
74
+ assert t.arrow_schema == pa.schema([('num', pa.int64()), ('bool', pa.bool_()), ('varch', pa.string())])
75
+
76
+ with pytest.raises(InvalidArgument):
77
+ util.create_table_from_files(s, 't2', different_schema_files)
78
+
79
+ with pytest.raises(InvalidArgument):
80
+ util.create_table_from_files(s, 't2', contained_schema_files, schema_merge_func=util.strict_schema_merge)
81
+
82
+ util.create_table_from_files(s, 't2', different_schema_files, schema_merge_func=util.union_schema_merge)
83
+ util.create_table_from_files(s, 't3', same_schema_files, schema_merge_func=util.strict_schema_merge)
84
+
85
+
86
+ def test_import_name_mismatch_error(session, clean_bucket_name, s3):
87
+ ds = {'varch': ['a', 'b', 'c'],
88
+ 'invalid_column_name': [1, 2, 3]}
89
+ prq_name = 'name_mismatch.parquet'
90
+ table = pa.Table.from_pydict(ds)
91
+ with NamedTemporaryFile() as f:
92
+ pq.write_table(table, f.name)
93
+ s3.put_object(Bucket=clean_bucket_name, Key=prq_name, Body=f)
94
+
95
+ with session.transaction() as tx:
96
+ b = tx.bucket(clean_bucket_name)
97
+ s = b.create_schema('s1')
98
+ t = s.create_table('t1', pa.schema([('varch', pa.string()), ('num', pa.int64())]))
99
+ with pytest.raises(ImportFilesError) as exc:
100
+ t.import_files([f'/{clean_bucket_name}/{prq_name}'])
101
+ assert exc.value.error_dict['object_name'] == prq_name
102
+ assert exc.value.error_dict['res'] == 'TabularMismatchColumnName'
103
+ assert 'invalid_column_name' in exc.value.error_dict['err_msg']
104
+
105
+
106
+ def test_import_type_mismatch_error(session, clean_bucket_name, s3):
107
+ ds = {'varch': ['a', 'b', 'c'],
108
+ 'num_type_mismatch': [1, 2, 3]}
109
+ prq_name = 'type_mismatch.parquet'
110
+ table = pa.Table.from_pydict(ds)
111
+ with NamedTemporaryFile() as f:
112
+ pq.write_table(table, f.name)
113
+ s3.put_object(Bucket=clean_bucket_name, Key=prq_name, Body=f)
114
+
115
+ with session.transaction() as tx:
116
+ b = tx.bucket(clean_bucket_name)
117
+ s = b.create_schema('s1')
118
+ t = s.create_table('t1', pa.schema([('varch', pa.string()), ('num_type_mismatch', pa.bool_())]))
119
+ with pytest.raises(ImportFilesError) as exc:
120
+ t.import_files([f'/{clean_bucket_name}/{prq_name}'])
121
+ assert exc.value.error_dict['object_name'] == prq_name
122
+ assert exc.value.error_dict['res'] == 'TabularMismatchColumnType'
123
+ assert 'num_type_mismatch' in exc.value.error_dict['err_msg']
@@ -0,0 +1,28 @@
1
+ import itertools
2
+
3
+ import pyarrow as pa
4
+
5
+ from .util import prepare_data
6
+
7
+
8
+ def test_nested(session, clean_bucket_name):
9
+ columns = pa.schema([
10
+ ('l', pa.list_(pa.int8())),
11
+ ('m', pa.map_(pa.utf8(), pa.float64())),
12
+ ('s', pa.struct([('x', pa.int16()), ('y', pa.int32())])),
13
+ ])
14
+ expected = pa.table(schema=columns, data=[
15
+ [[1], [], [2, 3], None],
16
+ [None, {'a': 2.5}, {'b': 0.25, 'c': 0.025}, {}],
17
+ [{'x': 1, 'y': None}, None, {'x': 2, 'y': 3}, {'x': None, 'y': 4}],
18
+ ])
19
+
20
+ with prepare_data(session, clean_bucket_name, 's', 't', expected) as t:
21
+ actual = pa.Table.from_batches(t.select())
22
+ assert actual == expected
23
+
24
+ names = [f.name for f in columns]
25
+ for n in range(len(names) + 1):
26
+ for cols in itertools.permutations(names, n):
27
+ actual = pa.Table.from_batches(t.select(columns=cols))
28
+ assert actual == expected.select(cols)
@@ -0,0 +1,42 @@
1
+ import logging
2
+
3
+ import pyarrow as pa
4
+
5
+ log = logging.getLogger(__name__)
6
+
7
+ def test_basic_projections(session, clean_bucket_name):
8
+ with session.transaction() as tx:
9
+ s = tx.bucket(clean_bucket_name).create_schema('s1')
10
+ columns = pa.schema([
11
+ ('a', pa.int8()),
12
+ ('b', pa.int16()),
13
+ ('c', pa.string()),
14
+ ('d', pa.int16()),
15
+ ('e', pa.int64()),
16
+ ('s', pa.struct([('x', pa.int8()), ('y', pa.int16())]))
17
+ ])
18
+
19
+ assert s.tables() == []
20
+ t = s.create_table('t1', columns)
21
+ assert s.tables() == [t]
22
+
23
+ sorted_columns = ['a']
24
+ unsorted_columns = ['b']
25
+ p1 = t.create_projection('p1', sorted_columns, unsorted_columns)
26
+
27
+ sorted_columns = ['b']
28
+ unsorted_columns = ['c', 'd']
29
+ p2 = t.create_projection('p2', sorted_columns, unsorted_columns)
30
+
31
+ projs = t.projections()
32
+ assert projs == [t.projection('p1'), t.projection('p2')]
33
+ p1 = t.projection('p1')
34
+ assert p1.name == 'p1'
35
+ p2 = t.projection('p2')
36
+ assert p2.name == 'p2'
37
+
38
+ p1.rename('p_new')
39
+ p2.drop()
40
+ projs = t.projections()
41
+ assert len(projs) == 1
42
+ assert projs[0].name == 'p_new'
@@ -1,26 +1,44 @@
1
+ import contextlib
1
2
  import logging
2
-
3
3
  import threading
4
- from http.server import HTTPServer, BaseHTTPRequestHandler
5
- from vastdb import api
4
+ from http.server import BaseHTTPRequestHandler, HTTPServer
6
5
  from itertools import cycle
7
6
 
7
+ import pytest
8
+ import requests
9
+
10
+ import vastdb
11
+
8
12
  log = logging.getLogger(__name__)
9
13
 
10
- def test_hello_world(rpc):
11
- with rpc.transaction() as tx:
14
+
15
+ def test_hello_world(session):
16
+ with session.transaction() as tx:
12
17
  assert tx.txid is not None
13
18
 
19
+
20
+ def test_bad_credentials(session):
21
+ bad_session = vastdb.connect(access='BAD', secret='BAD', endpoint=session.api.url)
22
+ with pytest.raises(vastdb.errors.Forbidden):
23
+ with bad_session.transaction():
24
+ pass
25
+
26
+
27
+ def test_bad_endpoint(session):
28
+ with pytest.raises(requests.exceptions.ConnectionError):
29
+ vastdb.connect(access='BAD', secret='BAD', endpoint='http://invalid-host-name-for-tests:12345')
30
+
31
+
14
32
  def test_version_extraction():
15
33
  # A list of version and expected version parsed by API
16
34
  TEST_CASES = [
17
- (None, None), # vast server without version in header
18
- ("5", None), # major only is not supported
19
- ("5.2", "5.2"), # major.minor
20
- ("5.2.0", "5.2.0"), # major.minor.patch
21
- ("5.2.0.0", "5.2.0.0"), # major.minor.patch.protocol
22
- ("5.2.0.0 some other things", "5.2.0.0"), # Test forward comptibility 1
23
- ("5.2.0.0.20 some other things", "5.2.0.0"), # Test forward comptibility 2
35
+ (None, None), # vast server without version in header
36
+ ("5", None), # major
37
+ ("5.2", None), # major.minor
38
+ ("5.2.0", None), # major.minor.patch
39
+ ("5.2.0.10", "5.2.0.10"), # major.minor.patch.protocol
40
+ ("5.2.0.10 some other things", None), # suffix
41
+ ("5.2.0.10.20", None), # extra version
24
42
  ]
25
43
 
26
44
  # Mock OPTIONS handle that cycles through the test cases response
@@ -55,9 +73,10 @@ def test_version_extraction():
55
73
  server_thread.start()
56
74
 
57
75
  try:
58
- for test_case in TEST_CASES:
59
- tester = api.VastdbApi(endpoint=f"http://localhost:{httpd.server_port}", access_key="abc", secret_key="abc")
60
- assert tester.vast_version == test_case[1]
76
+ for _, expected in TEST_CASES:
77
+ with (pytest.raises(NotImplementedError) if expected is None else contextlib.nullcontext()):
78
+ s = vastdb.connect(endpoint=f"http://localhost:{httpd.server_port}", access="abc", secret="abc")
79
+ assert s.api.vast_version == expected
61
80
  finally:
62
81
  # make sure we shut the server down no matter what
63
82
  httpd.shutdown()
@@ -1,8 +1,10 @@
1
1
  import pytest
2
2
 
3
+ from .. import errors
3
4
 
4
- def test_schemas(rpc, clean_bucket_name):
5
- with rpc.transaction() as tx:
5
+
6
+ def test_schemas(session, clean_bucket_name):
7
+ with session.transaction() as tx:
6
8
  b = tx.bucket(clean_bucket_name)
7
9
  assert b.schemas() == []
8
10
 
@@ -19,8 +21,24 @@ def test_schemas(rpc, clean_bucket_name):
19
21
  assert b.schemas() == []
20
22
 
21
23
 
22
- def test_commits_and_rollbacks(rpc, clean_bucket_name):
23
- with rpc.transaction() as tx:
24
+ def test_exists(session, clean_bucket_name):
25
+ with session.transaction() as tx:
26
+ b = tx.bucket(clean_bucket_name)
27
+ assert b.schemas() == []
28
+
29
+ s = b.create_schema('s1')
30
+
31
+ assert b.schemas() == [s]
32
+ with pytest.raises(errors.SchemaExists):
33
+ b.create_schema('s1')
34
+
35
+ assert b.schemas() == [s]
36
+ assert b.create_schema('s1', fail_if_exists=False) == s
37
+ assert b.schemas() == [s]
38
+
39
+
40
+ def test_commits_and_rollbacks(session, clean_bucket_name):
41
+ with session.transaction() as tx:
24
42
  b = tx.bucket(clean_bucket_name)
25
43
  assert b.schemas() == []
26
44
  b.create_schema("s3")
@@ -28,12 +46,18 @@ def test_commits_and_rollbacks(rpc, clean_bucket_name):
28
46
  # implicit commit
29
47
 
30
48
  with pytest.raises(ZeroDivisionError):
31
- with rpc.transaction() as tx:
49
+ with session.transaction() as tx:
32
50
  b = tx.bucket(clean_bucket_name)
33
51
  b.schema("s3").drop()
34
52
  assert b.schemas() == []
35
53
  1/0 # rollback schema dropping
36
54
 
37
- with rpc.transaction() as tx:
55
+ with session.transaction() as tx:
38
56
  b = tx.bucket(clean_bucket_name)
39
57
  assert b.schemas() != []
58
+
59
+ def test_list_snapshots(session, clean_bucket_name):
60
+ with session.transaction() as tx:
61
+ b = tx.bucket(clean_bucket_name)
62
+ s = b.snapshots()
63
+ assert s == []