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.
- vast_flatbuf/tabular/GetTableStatsResponse.py +45 -1
- vast_flatbuf/tabular/VipRange.py +56 -0
- vastdb/__init__.py +7 -0
- vastdb/bench/test_perf.py +29 -0
- vastdb/bucket.py +85 -0
- vastdb/{tests/conftest.py → conftest.py} +29 -14
- vastdb/errors.py +175 -0
- vastdb/{api.py → internal_commands.py} +373 -875
- vastdb/schema.py +85 -0
- vastdb/session.py +47 -0
- vastdb/table.py +483 -0
- vastdb/tests/test_imports.py +123 -0
- vastdb/tests/test_nested.py +28 -0
- vastdb/tests/test_projections.py +42 -0
- vastdb/tests/test_sanity.py +34 -15
- vastdb/tests/test_schemas.py +30 -6
- vastdb/tests/test_tables.py +628 -13
- vastdb/tests/util.py +18 -0
- vastdb/transaction.py +54 -0
- vastdb/util.py +11 -10
- vastdb-0.1.1.dist-info/METADATA +38 -0
- {vastdb-0.0.5.3.dist-info → vastdb-0.1.1.dist-info}/RECORD +26 -31
- vast_protobuf/substrait/__init__.py +0 -0
- vast_protobuf/substrait/algebra_pb2.py +0 -1344
- vast_protobuf/substrait/capabilities_pb2.py +0 -46
- vast_protobuf/substrait/ddl_pb2.py +0 -57
- vast_protobuf/substrait/extended_expression_pb2.py +0 -49
- vast_protobuf/substrait/extensions/__init__.py +0 -0
- vast_protobuf/substrait/extensions/extensions_pb2.py +0 -89
- vast_protobuf/substrait/function_pb2.py +0 -168
- vast_protobuf/substrait/parameterized_types_pb2.py +0 -181
- vast_protobuf/substrait/plan_pb2.py +0 -67
- vast_protobuf/substrait/type_expressions_pb2.py +0 -198
- vast_protobuf/substrait/type_pb2.py +0 -350
- vast_protobuf/tabular/__init__.py +0 -0
- vast_protobuf/tabular/rpc_pb2.py +0 -344
- vastdb/bench_scan.py +0 -45
- vastdb/tests/test_create_table_from_parquets.py +0 -50
- vastdb/v2.py +0 -360
- vastdb-0.0.5.3.dist-info/METADATA +0 -47
- {vast_protobuf → vastdb/bench}/__init__.py +0 -0
- {vastdb-0.0.5.3.dist-info → vastdb-0.1.1.dist-info}/LICENSE +0 -0
- {vastdb-0.0.5.3.dist-info → vastdb-0.1.1.dist-info}/WHEEL +0 -0
- {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'
|
vastdb/tests/test_sanity.py
CHANGED
|
@@ -1,26 +1,44 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import logging
|
|
2
|
-
|
|
3
3
|
import threading
|
|
4
|
-
from http.server import
|
|
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
|
-
|
|
11
|
-
|
|
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),
|
|
18
|
-
("5", None), # major
|
|
19
|
-
("5.2",
|
|
20
|
-
("5.2.0",
|
|
21
|
-
("5.2.0.
|
|
22
|
-
("5.2.0.
|
|
23
|
-
("5.2.0.
|
|
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
|
|
59
|
-
|
|
60
|
-
|
|
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()
|
vastdb/tests/test_schemas.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import pytest
|
|
2
2
|
|
|
3
|
+
from .. import errors
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
23
|
-
with
|
|
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
|
|
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
|
|
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 == []
|