vastdb 1.3.2__py3-none-any.whl → 1.3.4__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.
@@ -0,0 +1,104 @@
1
+ # automatically generated by the FlatBuffers compiler, do not modify
2
+
3
+ # namespace: tabular
4
+
5
+ import flatbuffers
6
+ from flatbuffers.compat import import_numpy
7
+ np = import_numpy()
8
+
9
+ class CreateViewRequest(object):
10
+ __slots__ = ['_tab']
11
+
12
+ @classmethod
13
+ def GetRootAs(cls, buf, offset=0):
14
+ n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
15
+ x = CreateViewRequest()
16
+ x.Init(buf, n + offset)
17
+ return x
18
+
19
+ @classmethod
20
+ def GetRootAsCreateViewRequest(cls, buf, offset=0):
21
+ """This method is deprecated. Please switch to GetRootAs."""
22
+ return cls.GetRootAs(buf, offset)
23
+ # CreateViewRequest
24
+ def Init(self, buf, pos):
25
+ self._tab = flatbuffers.table.Table(buf, pos)
26
+
27
+ # CreateViewRequest
28
+ def ViewMetadataArrowBuffer(self, j):
29
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
30
+ if o != 0:
31
+ a = self._tab.Vector(o)
32
+ return self._tab.Get(flatbuffers.number_types.Uint8Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 1))
33
+ return 0
34
+
35
+ # CreateViewRequest
36
+ def ViewMetadataArrowBufferAsNumpy(self):
37
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
38
+ if o != 0:
39
+ return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Uint8Flags, o)
40
+ return 0
41
+
42
+ # CreateViewRequest
43
+ def ViewMetadataArrowBufferLength(self):
44
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
45
+ if o != 0:
46
+ return self._tab.VectorLen(o)
47
+ return 0
48
+
49
+ # CreateViewRequest
50
+ def ViewMetadataArrowBufferIsNone(self):
51
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
52
+ return o == 0
53
+
54
+ # CreateViewRequest
55
+ def ViewDataArrowSchema(self, j):
56
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6))
57
+ if o != 0:
58
+ a = self._tab.Vector(o)
59
+ return self._tab.Get(flatbuffers.number_types.Uint8Flags, a + flatbuffers.number_types.UOffsetTFlags.py_type(j * 1))
60
+ return 0
61
+
62
+ # CreateViewRequest
63
+ def ViewDataArrowSchemaAsNumpy(self):
64
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6))
65
+ if o != 0:
66
+ return self._tab.GetVectorAsNumpy(flatbuffers.number_types.Uint8Flags, o)
67
+ return 0
68
+
69
+ # CreateViewRequest
70
+ def ViewDataArrowSchemaLength(self):
71
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6))
72
+ if o != 0:
73
+ return self._tab.VectorLen(o)
74
+ return 0
75
+
76
+ # CreateViewRequest
77
+ def ViewDataArrowSchemaIsNone(self):
78
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6))
79
+ return o == 0
80
+
81
+ def Start(builder): builder.StartObject(2)
82
+ def CreateViewRequestStart(builder):
83
+ """This method is deprecated. Please switch to Start."""
84
+ return Start(builder)
85
+ def AddViewMetadataArrowBuffer(builder, viewMetadataArrowBuffer): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(viewMetadataArrowBuffer), 0)
86
+ def CreateViewRequestAddViewMetadataArrowBuffer(builder, viewMetadataArrowBuffer):
87
+ """This method is deprecated. Please switch to AddViewMetadataArrowBuffer."""
88
+ return AddViewMetadataArrowBuffer(builder, viewMetadataArrowBuffer)
89
+ def StartViewMetadataArrowBufferVector(builder, numElems): return builder.StartVector(1, numElems, 1)
90
+ def CreateViewRequestStartViewMetadataArrowBufferVector(builder, numElems):
91
+ """This method is deprecated. Please switch to Start."""
92
+ return StartViewMetadataArrowBufferVector(builder, numElems)
93
+ def AddViewDataArrowSchema(builder, viewDataArrowSchema): builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(viewDataArrowSchema), 0)
94
+ def CreateViewRequestAddViewDataArrowSchema(builder, viewDataArrowSchema):
95
+ """This method is deprecated. Please switch to AddViewDataArrowSchema."""
96
+ return AddViewDataArrowSchema(builder, viewDataArrowSchema)
97
+ def StartViewDataArrowSchemaVector(builder, numElems): return builder.StartVector(1, numElems, 1)
98
+ def CreateViewRequestStartViewDataArrowSchemaVector(builder, numElems):
99
+ """This method is deprecated. Please switch to Start."""
100
+ return StartViewDataArrowSchemaVector(builder, numElems)
101
+ def End(builder): return builder.EndObject()
102
+ def CreateViewRequestEnd(builder):
103
+ """This method is deprecated. Please switch to End."""
104
+ return End(builder)
@@ -0,0 +1,89 @@
1
+ # automatically generated by the FlatBuffers compiler, do not modify
2
+
3
+ # namespace: tabular
4
+
5
+ import flatbuffers
6
+ from flatbuffers.compat import import_numpy
7
+ np = import_numpy()
8
+
9
+ class ListViewsResponse(object):
10
+ __slots__ = ['_tab']
11
+
12
+ @classmethod
13
+ def GetRootAs(cls, buf, offset=0):
14
+ n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset)
15
+ x = ListViewsResponse()
16
+ x.Init(buf, n + offset)
17
+ return x
18
+
19
+ @classmethod
20
+ def GetRootAsListViewsResponse(cls, buf, offset=0):
21
+ """This method is deprecated. Please switch to GetRootAs."""
22
+ return cls.GetRootAs(buf, offset)
23
+ # ListViewsResponse
24
+ def Init(self, buf, pos):
25
+ self._tab = flatbuffers.table.Table(buf, pos)
26
+
27
+ # ListViewsResponse
28
+ def BucketName(self):
29
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4))
30
+ if o != 0:
31
+ return self._tab.String(o + self._tab.Pos)
32
+ return None
33
+
34
+ # ListViewsResponse
35
+ def SchemaName(self):
36
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6))
37
+ if o != 0:
38
+ return self._tab.String(o + self._tab.Pos)
39
+ return None
40
+
41
+ # ListViewsResponse
42
+ def Views(self, j):
43
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
44
+ if o != 0:
45
+ x = self._tab.Vector(o)
46
+ x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * 4
47
+ x = self._tab.Indirect(x)
48
+ from vast_flatbuf.tabular.ObjectDetails import ObjectDetails
49
+ obj = ObjectDetails()
50
+ obj.Init(self._tab.Bytes, x)
51
+ return obj
52
+ return None
53
+
54
+ # ListViewsResponse
55
+ def ViewsLength(self):
56
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
57
+ if o != 0:
58
+ return self._tab.VectorLen(o)
59
+ return 0
60
+
61
+ # ListViewsResponse
62
+ def ViewsIsNone(self):
63
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8))
64
+ return o == 0
65
+
66
+ def Start(builder): builder.StartObject(3)
67
+ def ListViewsResponseStart(builder):
68
+ """This method is deprecated. Please switch to Start."""
69
+ return Start(builder)
70
+ def AddBucketName(builder, bucketName): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(bucketName), 0)
71
+ def ListViewsResponseAddBucketName(builder, bucketName):
72
+ """This method is deprecated. Please switch to AddBucketName."""
73
+ return AddBucketName(builder, bucketName)
74
+ def AddSchemaName(builder, schemaName): builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(schemaName), 0)
75
+ def ListViewsResponseAddSchemaName(builder, schemaName):
76
+ """This method is deprecated. Please switch to AddSchemaName."""
77
+ return AddSchemaName(builder, schemaName)
78
+ def AddViews(builder, views): builder.PrependUOffsetTRelativeSlot(2, flatbuffers.number_types.UOffsetTFlags.py_type(views), 0)
79
+ def ListViewsResponseAddViews(builder, views):
80
+ """This method is deprecated. Please switch to AddViews."""
81
+ return AddViews(builder, views)
82
+ def StartViewsVector(builder, numElems): return builder.StartVector(4, numElems, 4)
83
+ def ListViewsResponseStartViewsVector(builder, numElems):
84
+ """This method is deprecated. Please switch to Start."""
85
+ return StartViewsVector(builder, numElems)
86
+ def End(builder): return builder.EndObject()
87
+ def ListViewsResponseEnd(builder):
88
+ """This method is deprecated. Please switch to End."""
89
+ return End(builder)
@@ -60,7 +60,21 @@ class ObjectDetails(object):
60
60
  return self._tab.Get(flatbuffers.number_types.Uint64Flags, o + self._tab.Pos)
61
61
  return 0
62
62
 
63
- def Start(builder): builder.StartObject(5)
63
+ # ObjectDetails
64
+ def NumPartitions(self):
65
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(14))
66
+ if o != 0:
67
+ return self._tab.Get(flatbuffers.number_types.Uint64Flags, o + self._tab.Pos)
68
+ return 0
69
+
70
+ # ObjectDetails
71
+ def SortingKeyEnabled(self):
72
+ o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(16))
73
+ if o != 0:
74
+ return bool(self._tab.Get(flatbuffers.number_types.BoolFlags, o + self._tab.Pos))
75
+ return False
76
+
77
+ def Start(builder): builder.StartObject(7)
64
78
  def ObjectDetailsStart(builder):
65
79
  """This method is deprecated. Please switch to Start."""
66
80
  return Start(builder)
@@ -84,6 +98,14 @@ def AddSizeInBytes(builder, sizeInBytes): builder.PrependUint64Slot(4, sizeInByt
84
98
  def ObjectDetailsAddSizeInBytes(builder, sizeInBytes):
85
99
  """This method is deprecated. Please switch to AddSizeInBytes."""
86
100
  return AddSizeInBytes(builder, sizeInBytes)
101
+ def AddNumPartitions(builder, numPartitions): builder.PrependUint64Slot(5, numPartitions, 0)
102
+ def ObjectDetailsAddNumPartitions(builder, numPartitions):
103
+ """This method is deprecated. Please switch to AddNumPartitions."""
104
+ return AddNumPartitions(builder, numPartitions)
105
+ def AddSortingKeyEnabled(builder, sortingKeyEnabled): builder.PrependBoolSlot(6, sortingKeyEnabled, 0)
106
+ def ObjectDetailsAddSortingKeyEnabled(builder, sortingKeyEnabled):
107
+ """This method is deprecated. Please switch to AddSortingKeyEnabled."""
108
+ return AddSortingKeyEnabled(builder, sortingKeyEnabled)
87
109
  def End(builder): return builder.EndObject()
88
110
  def ObjectDetailsEnd(builder):
89
111
  """This method is deprecated. Please switch to End."""
vastdb/_internal.py CHANGED
@@ -58,6 +58,10 @@ import vast_flatbuf.org.apache.arrow.computeir.flatbuf.Source as fb_source
58
58
  import vast_flatbuf.org.apache.arrow.computeir.flatbuf.StringLiteral as fb_string_lit
59
59
  import vast_flatbuf.org.apache.arrow.computeir.flatbuf.TimeLiteral as fb_time_lit
60
60
  import vast_flatbuf.org.apache.arrow.computeir.flatbuf.TimestampLiteral as fb_timestamp_lit
61
+ import vast_flatbuf.org.apache.arrow.computeir.flatbuf.UInt8Literal as fb_uint8_lit
62
+ import vast_flatbuf.org.apache.arrow.computeir.flatbuf.UInt16Literal as fb_uint16_lit
63
+ import vast_flatbuf.org.apache.arrow.computeir.flatbuf.UInt32Literal as fb_uint32_lit
64
+ import vast_flatbuf.org.apache.arrow.computeir.flatbuf.UInt64Literal as fb_uint64_lit
61
65
  import vast_flatbuf.org.apache.arrow.flatbuf.Binary as fb_binary
62
66
  import vast_flatbuf.org.apache.arrow.flatbuf.Bool as fb_bool
63
67
  import vast_flatbuf.org.apache.arrow.flatbuf.Date as fb_date
@@ -341,50 +345,57 @@ class Predicate:
341
345
  fb_expression.AddImpl(self.builder, offset_call)
342
346
  return fb_expression.End(self.builder)
343
347
 
348
+ # see https://github.com/apache/arrow/blob/main/format/Schema.fbs
349
+ # https://github.com/apache/arrow/blob/apache-arrow-7.0.0/experimental/computeir/Expression.fbs
350
+ # https://github.com/apache/arrow/blob/apache-arrow-7.0.0/experimental/computeir/Literal.fbs
344
351
  def build_literal(self, field: pa.Field, value):
345
352
  literal_type: Any
346
353
 
347
- if field.type.equals(pa.int64()):
348
- literal_type = fb_int64_lit
349
- literal_impl = LiteralImpl.Int64Literal
354
+ if field.type.equals(pa.int64()) or field.type.equals(pa.uint64()):
355
+ is_signed = field.type.equals(pa.int64())
356
+ literal_type = fb_int64_lit if is_signed else fb_uint64_lit
357
+ literal_impl = LiteralImpl.Int64Literal if is_signed else LiteralImpl.UInt64Literal
350
358
 
351
359
  field_type_type = Type.Int
352
360
  fb_int.Start(self.builder)
353
361
  fb_int.AddBitWidth(self.builder, field.type.bit_width)
354
- fb_int.AddIsSigned(self.builder, True)
362
+ fb_int.AddIsSigned(self.builder, is_signed)
355
363
  field_type = fb_int.End(self.builder)
356
364
 
357
365
  value = int(value)
358
- elif field.type.equals(pa.int32()):
359
- literal_type = fb_int32_lit
360
- literal_impl = LiteralImpl.Int32Literal
366
+ elif field.type.equals(pa.int32()) or field.type.equals(pa.uint32()):
367
+ is_signed = field.type.equals(pa.int32())
368
+ literal_type = fb_int32_lit if is_signed else fb_uint32_lit
369
+ literal_impl = LiteralImpl.Int32Literal if is_signed else LiteralImpl.UInt32Literal
361
370
 
362
371
  field_type_type = Type.Int
363
372
  fb_int.Start(self.builder)
364
373
  fb_int.AddBitWidth(self.builder, field.type.bit_width)
365
- fb_int.AddIsSigned(self.builder, True)
374
+ fb_int.AddIsSigned(self.builder, is_signed)
366
375
  field_type = fb_int.End(self.builder)
367
376
 
368
377
  value = int(value)
369
- elif field.type.equals(pa.int16()):
370
- literal_type = fb_int16_lit
371
- literal_impl = LiteralImpl.Int16Literal
378
+ elif field.type.equals(pa.int16()) or field.type.equals(pa.uint16()):
379
+ is_signed = field.type.equals(pa.int16())
380
+ literal_type = fb_int16_lit if is_signed else fb_uint16_lit
381
+ literal_impl = LiteralImpl.Int16Literal if is_signed else LiteralImpl.UInt16Literal
372
382
 
373
383
  field_type_type = Type.Int
374
384
  fb_int.Start(self.builder)
375
385
  fb_int.AddBitWidth(self.builder, field.type.bit_width)
376
- fb_int.AddIsSigned(self.builder, True)
386
+ fb_int.AddIsSigned(self.builder, is_signed)
377
387
  field_type = fb_int.End(self.builder)
378
388
 
379
389
  value = int(value)
380
- elif field.type.equals(pa.int8()):
381
- literal_type = fb_int8_lit
382
- literal_impl = LiteralImpl.Int8Literal
390
+ elif field.type.equals(pa.int8()) or field.type.equals(pa.uint8()):
391
+ is_signed = field.type.equals(pa.int8())
392
+ literal_type = fb_int8_lit if is_signed else fb_uint8_lit
393
+ literal_impl = LiteralImpl.Int8Literal if is_signed else LiteralImpl.UInt8Literal
383
394
 
384
395
  field_type_type = Type.Int
385
396
  fb_int.Start(self.builder)
386
397
  fb_int.AddBitWidth(self.builder, field.type.bit_width)
387
- fb_int.AddIsSigned(self.builder, True)
398
+ fb_int.AddIsSigned(self.builder, is_signed)
388
399
  field_type = fb_int.End(self.builder)
389
400
 
390
401
  value = int(value)
@@ -735,17 +746,57 @@ def _iter_nested_arrays(column: pa.Array) -> Iterator[pa.Array]:
735
746
  yield from _iter_nested_arrays(column.values) # Note: Map is serialized in VAST as a List<Struct<K, V>>
736
747
 
737
748
 
738
- TableInfo = namedtuple('TableInfo', 'name properties handle num_rows size_in_bytes')
749
+ class ValidateInList:
750
+ def __init__(self, *args):
751
+ self.candidates = [x.strip() for x in args]
739
752
 
753
+ def __call__(self, x):
754
+ x = x.strip()
755
+ if x not in self.candidates:
756
+ raise Exception(f'{x} is not in {self.candidates}')
757
+ return x
758
+
759
+ def __getitem__(self, x):
760
+ return self
761
+
762
+
763
+ _int_coding = (lambda x: str(int(x)), lambda x: int(x))
764
+ _prop_coding = {
765
+ "message.timestamp.type": ValidateInList('CreateTime', 'LogAppendTime'),
766
+ "retention.ms": _int_coding,
767
+ "message.timestamp.after.max.ms": _int_coding,
768
+ "message.timestamp.before.max.ms": _int_coding
769
+ }
770
+
771
+
772
+ def _encode_table_props(**kwargs):
773
+ if all([v is None for v in kwargs.values()]):
774
+ return None
775
+ else:
776
+ pairs = [(k.replace("_", ".").strip(), v) for k, v in kwargs.items() if v is not None]
777
+ return "$".join([f"{k}={_prop_coding[k][0](v)}" for k, v in pairs])
778
+
779
+
780
+ def _decode_table_props(s):
781
+ if s.strip() == '':
782
+ return {}
783
+ triplets = [(x.strip(), x.strip().replace(".", "_"), y.strip()) for x, y in [z.split('=') for z in s.strip().split("$")]]
784
+ return {y: _prop_coding[x][1](z) for x, y, z in triplets if z != ''}
740
785
 
741
- def _parse_table_info(obj):
742
786
 
787
+ TableInfo = namedtuple('TableInfo', 'name properties handle num_rows size_in_bytes num_partitions')
788
+
789
+
790
+ def _parse_table_info(obj):
743
791
  name = obj.Name().decode()
744
792
  properties = obj.Properties().decode()
745
793
  handle = obj.Handle().decode()
746
794
  num_rows = obj.NumRows()
747
795
  used_bytes = obj.SizeInBytes()
748
- return TableInfo(name, properties, handle, num_rows, used_bytes)
796
+ num_partitions = obj.NumPartitions()
797
+ if num_partitions != 0:
798
+ properties = _decode_table_props(properties)
799
+ return TableInfo(name, properties, handle, num_rows, used_bytes, num_partitions)
749
800
 
750
801
 
751
802
  # Results that returns from tablestats
@@ -774,7 +825,13 @@ def _backoff_giveup(exc: Exception) -> bool:
774
825
  return True # give up in case of other exceptions
775
826
 
776
827
 
828
+ class UnsupportedServer(NotImplementedError):
829
+ """Raised when the response comes back from non-VAST DB server."""
830
+ pass
831
+
832
+
777
833
  class VastdbApi:
834
+ VAST_SERVER_PREFIX = 'vast'
778
835
  # we expect the vast version to be <major>.<minor>.<patch>.<protocol>
779
836
  VAST_VERSION_REGEX = re.compile(r'^vast (\d+\.\d+\.\d+\.\d+)$')
780
837
 
@@ -829,6 +886,9 @@ class VastdbApi:
829
886
  if server_header is None:
830
887
  _logger.error("Response doesn't contain 'Server' header")
831
888
  else:
889
+ if not server_header.startswith(self.VAST_SERVER_PREFIX):
890
+ raise UnsupportedServer(f'{self.url} is not a VAST DB server endpoint ("{server_header}")')
891
+
832
892
  if m := self.VAST_VERSION_REGEX.match(server_header):
833
893
  self.vast_version: Tuple[int, ...] = tuple(int(v) for v in m.group(1).split("."))
834
894
  return
@@ -1049,8 +1109,9 @@ class VastdbApi:
1049
1109
 
1050
1110
  return snapshots, is_truncated, marker
1051
1111
 
1052
- def create_table(self, bucket, schema, name, arrow_schema, txid=0, client_tags=[], expected_retvals=[],
1053
- topic_partitions=0, create_imports_table=False, use_external_row_ids_allocation=False):
1112
+ def create_table(self, bucket, schema, name, arrow_schema=None, txid=0, client_tags=[], expected_retvals=[],
1113
+ topic_partitions=0, create_imports_table=False, use_external_row_ids_allocation=False,
1114
+ message_timestamp_type=None, retention_ms=None, message_timestamp_after_max_ms=None, message_timestamp_before_max_ms=None):
1054
1115
  """
1055
1116
  Create a table, use the following request
1056
1117
  POST /bucket/schema/table?table HTTP/1.1
@@ -1069,6 +1130,9 @@ class VastdbApi:
1069
1130
  """
1070
1131
  headers = self._fill_common_headers(txid=txid, client_tags=client_tags)
1071
1132
 
1133
+ if arrow_schema is None:
1134
+ arrow_schema = pa.schema([])
1135
+
1072
1136
  serialized_schema = arrow_schema.serialize()
1073
1137
  headers['Content-Length'] = str(len(serialized_schema))
1074
1138
  if use_external_row_ids_allocation:
@@ -1078,6 +1142,11 @@ class VastdbApi:
1078
1142
  if create_imports_table:
1079
1143
  url_params['sub-table'] = IMPORTED_OBJECTS_TABLE_NAME
1080
1144
 
1145
+ if topic_partitions > 0:
1146
+ table_props = _encode_table_props(message_timestamp_type=message_timestamp_type, retention_ms=retention_ms, message_timestamp_after_max_ms=message_timestamp_after_max_ms, message_timestamp_before_max_ms=message_timestamp_before_max_ms)
1147
+ if table_props is not None:
1148
+ url_params['table-props'] = table_props
1149
+
1081
1150
  self._request(
1082
1151
  method="POST",
1083
1152
  url=self._url(bucket=bucket, schema=schema, table=name, command="table", url_params=url_params),
@@ -1106,7 +1175,8 @@ class VastdbApi:
1106
1175
  return TableStatsResult(num_rows, size_in_bytes, is_external_rowid_alloc, tuple(endpoints))
1107
1176
 
1108
1177
  def alter_table(self, bucket, schema, name, txid=0, client_tags=[], table_properties="",
1109
- new_name="", expected_retvals=[]):
1178
+ new_name="", expected_retvals=[],
1179
+ message_timestamp_type=None, retention_ms=None, message_timestamp_after_max_ms=None, message_timestamp_before_max_ms=None):
1110
1180
  """
1111
1181
  PUT /mybucket/myschema/mytable?table HTTP/1.1
1112
1182
  Content-Length: ContentLength
@@ -1118,6 +1188,11 @@ class VastdbApi:
1118
1188
  """
1119
1189
  builder = flatbuffers.Builder(1024)
1120
1190
 
1191
+ if message_timestamp_type is not None or retention_ms is not None or message_timestamp_after_max_ms is not None or message_timestamp_before_max_ms is not None:
1192
+ table_properties = _encode_table_props(message_timestamp_type=message_timestamp_type, retention_ms=retention_ms, message_timestamp_after_max_ms=message_timestamp_after_max_ms, message_timestamp_before_max_ms=message_timestamp_before_max_ms)
1193
+ if table_properties is None:
1194
+ table_properties = ""
1195
+
1121
1196
  properties = builder.CreateString(table_properties)
1122
1197
  tabular_alter_table.Start(builder)
1123
1198
  if len(table_properties):
vastdb/schema.py CHANGED
@@ -146,5 +146,4 @@ class Schema:
146
146
 
147
147
 
148
148
  def _parse_table_info(table_info, schema: "schema.Schema"):
149
- stats = table.TableStats(num_rows=table_info.num_rows, size_in_bytes=table_info.size_in_bytes)
150
- return table.Table(name=table_info.name, schema=schema, handle=int(table_info.handle), stats=stats, _imports_table=False)
149
+ return table.Table(name=table_info.name, schema=schema, handle=int(table_info.handle), _imports_table=False)
vastdb/table.py CHANGED
@@ -112,7 +112,6 @@ class Table:
112
112
  name: str
113
113
  schema: "schema.Schema"
114
114
  handle: int
115
- stats: TableStats
116
115
  arrow_schema: pa.Schema = field(init=False, compare=False, repr=False)
117
116
  _ibis_table: ibis.Schema = field(init=False, compare=False, repr=False)
118
117
  _imports_table: bool
@@ -139,6 +138,11 @@ class Table:
139
138
  """Return bucket."""
140
139
  return self.schema.bucket
141
140
 
141
+ @property
142
+ def stats(self):
143
+ """Fetch table's statistics from server."""
144
+ return self.get_stats()
145
+
142
146
  def columns(self) -> pa.Schema:
143
147
  """Return columns' metadata."""
144
148
  fields = []
@@ -576,7 +580,7 @@ class Table:
576
580
  def imports_table(self) -> Optional["Table"]:
577
581
  """Get the imports table of this table."""
578
582
  self.tx._rpc.features.check_imports_table()
579
- return Table(name=self.name, schema=self.schema, handle=int(self.handle), stats=self.stats, _imports_table=True)
583
+ return Table(name=self.name, schema=self.schema, handle=int(self.handle), _imports_table=True)
580
584
 
581
585
  def __getitem__(self, col_name):
582
586
  """Allow constructing ibis-like column expressions from this table.
vastdb/tests/metrics.py CHANGED
@@ -29,7 +29,7 @@ class Table:
29
29
  self.conn = conn
30
30
  self.name = name
31
31
  columns = ", ".join(
32
- f"{f.name} {_MAP_SQLITE_TYPES[f.type]}"
32
+ f"{f.name} {_MAP_SQLITE_TYPES[f.type]}" # type: ignore
33
33
  for f in self.fields
34
34
  )
35
35
  cmd = f"CREATE TABLE {self.name} ({columns})"
@@ -7,6 +7,7 @@ from itertools import cycle
7
7
  import pytest
8
8
 
9
9
  import vastdb.errors
10
+ from vastdb._internal import UnsupportedServer
10
11
 
11
12
  log = logging.getLogger(__name__)
12
13
 
@@ -32,13 +33,14 @@ def test_bad_endpoint(session):
32
33
  def test_version_extraction():
33
34
  # A list of version and expected version parsed by API
34
35
  TEST_CASES = [
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
36
+ ("nginx", UnsupportedServer), # non-vast server
37
+ ("vast", NotImplementedError), # vast server without version in header
38
+ ("vast 5", NotImplementedError), # major
39
+ ("vast 5.2", NotImplementedError), # major.minor
40
+ ("vast 5.2.0", NotImplementedError), # major.minor.patch
41
+ ("vast 5.2.0.10", (5, 2, 0, 10)), # major.minor.patch.protocol
42
+ ("vast 5.2.0.10 some other things", NotImplementedError), # suffix
43
+ ("vast 5.2.0.10.20", NotImplementedError), # extra version
42
44
  ]
43
45
 
44
46
  # Mock OPTIONS handle that cycles through the test cases response
@@ -53,8 +55,7 @@ def test_version_extraction():
53
55
  self.end_headers()
54
56
 
55
57
  def version_string(self):
56
- version = next(self.versions_iterator)[0]
57
- return f"vast {version}" if version else "vast"
58
+ return next(self.versions_iterator)[0]
58
59
 
59
60
  def log_message(self, format, *args):
60
61
  log.debug(format, *args)
@@ -74,7 +75,10 @@ def test_version_extraction():
74
75
 
75
76
  try:
76
77
  for _, expected in TEST_CASES:
77
- with (pytest.raises(NotImplementedError) if expected is None else contextlib.nullcontext()):
78
+ manager = contextlib.nullcontext()
79
+ if isinstance(expected, type) and issubclass(expected, NotImplementedError):
80
+ manager = pytest.raises(expected)
81
+ with manager:
78
82
  s = vastdb.connect(endpoint=f"http://localhost:{httpd.server_port}", access="abc", secret="abc")
79
83
  assert s.api.vast_version == expected
80
84
  finally:
@@ -388,6 +388,33 @@ def test_types(session, clean_bucket_name):
388
388
  assert select(t['ts9'] == ts_literal) == expected.filter(pc.field('ts9') == ts_literal)
389
389
 
390
390
 
391
+ def test_unsigned_filters(session, clean_bucket_name):
392
+ columns = pa.schema([
393
+ ('a', pa.uint8()),
394
+ ('b', pa.uint16()),
395
+ ('c', pa.uint32()),
396
+ ('d', pa.uint64()),
397
+ ])
398
+
399
+ expected = pa.table(schema=columns, data=[
400
+ [1, 2, 3],
401
+ [11, 22, 33],
402
+ [111, 222, 333],
403
+ [1111, 2222, 3333],
404
+ ])
405
+
406
+ with prepare_data(session, clean_bucket_name, 's', 't', expected) as table:
407
+ def select(predicate):
408
+ return table.select(predicate=predicate).read_all()
409
+
410
+ assert select(True) == expected
411
+ for t in [table, ibis._]:
412
+ assert select(t['a'] > 2) == expected.filter(pc.field('a') > 2)
413
+ assert select(t['b'] > 22) == expected.filter(pc.field('b') > 22)
414
+ assert select(t['c'] > 222) == expected.filter(pc.field('c') > 222)
415
+ assert select(t['d'] > 2222) == expected.filter(pc.field('d') > 2222)
416
+
417
+
391
418
  def test_filters(session, clean_bucket_name):
392
419
  columns = pa.schema([
393
420
  ('a', pa.int32()),
@@ -452,8 +479,8 @@ def test_filters(session, clean_bucket_name):
452
479
  assert select((t['a'] > 111) | (t['a'] < 333) | (t['a'] == 777)) == expected.filter((pc.field('a') > 111) | (pc.field('a') < 333) | (pc.field('a') == 777))
453
480
 
454
481
  assert select(t['s'].isnull()) == expected.filter(pc.field('s').is_null())
455
- assert select((t['s'].isnull()) | (t['s'] == 'bb')) == expected.filter((pc.field('s').is_null()) | (pc.field('s') == 'bb'))
456
- assert select((t['s'].isnull()) & (t['b'] == 3.5)) == expected.filter((pc.field('s').is_null()) & (pc.field('b') == 3.5))
482
+ assert select((t['s'].isnull()) | (t['s'] == 'bb')) == expected.filter((pc.field('s').is_null()) | (pc.field('s') == 'bb'))
483
+ assert select((t['s'].isnull()) & (t['b'] == 3.5)) == expected.filter((pc.field('s').is_null()) & (pc.field('b') == 3.5))
457
484
 
458
485
  assert select(~t['s'].isnull()) == expected.filter(~pc.field('s').is_null())
459
486
  assert select(t['s'].contains('b')) == expected.filter(pc.field('s') == 'bb')
@@ -882,3 +909,84 @@ def test_starts_with(session, clean_bucket_name):
882
909
 
883
910
  res = table.select(predicate=(table['s'].startswith('ab')) & (table['i'] > 3)).read_all()
884
911
  assert res.to_pydict() == {'i': [4], 's': ['abd']}
912
+
913
+
914
+ def test_external_row_id(session, clean_bucket_name):
915
+ columns = [
916
+ ('vastdb_rowid', pa.int64()),
917
+ ('x', pa.float32()),
918
+ ('y', pa.utf8()),
919
+ ]
920
+
921
+ with session.transaction() as tx:
922
+ s = tx.bucket(clean_bucket_name).create_schema('s')
923
+
924
+ t = s.create_table('t1', pa.schema(columns))
925
+ assert not t.stats.is_external_rowid_alloc
926
+ t.insert(pa.record_batch(schema=pa.schema(columns), data=[[0], [1.5], ['ABC']]))
927
+ assert t.stats.is_external_rowid_alloc
928
+
929
+ t = s.create_table('t2', pa.schema(columns))
930
+ assert not t.stats.is_external_rowid_alloc
931
+ t.insert(pa.record_batch(schema=pa.schema(columns[1:]), data=[[1.5], ['ABC']]))
932
+ assert not t.stats.is_external_rowid_alloc
933
+
934
+
935
+ def test_multiple_contains_clauses(session, clean_bucket_name):
936
+ columns = pa.schema([
937
+ ('theint', pa.int32()),
938
+ ('thestring', pa.string()),
939
+ ('theotherstring', pa.string()),
940
+ ])
941
+
942
+ expected = pa.table(schema=columns, data=[
943
+ [111, 222, 333, 444, 555],
944
+ ['abc', 'efg', 'hij', 'klm', 'nop'],
945
+ ['abcd', 'bcde', 'cdef', 'defg', 'efgh'],
946
+ ])
947
+ with (prepare_data(session, clean_bucket_name, 's', 't', expected) as t):
948
+ failed_preds = [
949
+ lambda t: (t["thestring"].contains("b") | t["theotherstring"].contains("a")),
950
+ lambda t: (~t["thestring"].contains("a")),
951
+ ]
952
+
953
+ assert t.select(predicate=t["thestring"].contains("b")).read_all() == expected.filter(pc.match_substring(expected["thestring"], "b"))
954
+ assert (t.select(predicate=(t["thestring"].contains("b")) & (t["theotherstring"].startswith("a"))).read_all() ==
955
+ expected.filter(pc.and_(
956
+ pc.match_substring(expected["thestring"], "b"),
957
+ pc.starts_with(expected["thestring"], "a")
958
+ )))
959
+ assert (t.select(predicate=(t["thestring"].contains("b") & (t["thestring"].contains("y")))).read_all() ==
960
+ t.select(predicate=t["thestring"].contains("y") & (t["thestring"].contains("b"))).read_all())
961
+
962
+ assert (t.select(predicate=(t["thestring"].contains("o") & (t["theint"] > 500))).read_all() ==
963
+ pc.filter(expected, pc.and_(
964
+ pc.match_substring(expected["thestring"], "o"),
965
+ pc.greater(expected["theint"], 500)
966
+ )))
967
+ assert (t.select(predicate=((t["thestring"].contains("bc")) | (t["thestring"].contains("kl")) |
968
+ (t["thestring"] == "hi") | (t["thestring"].startswith("e")))).read_all() ==
969
+ expected.filter(
970
+ pc.or_(pc.or_(pc.match_substring(expected["thestring"], "bc"),
971
+ pc.match_substring(expected["thestring"], "kl")),
972
+ pc.or_(pc.equal(expected["thestring"], "hi"),
973
+ pc.starts_with(expected["thestring"], "e"))
974
+ )))
975
+ assert (t.select(predicate=((t["thestring"].contains("abc")) | (t["thestring"].contains("xyz")) |
976
+ (t["thestring"].startswith("z")) | (t["thestring"].isnull()))).read_all() ==
977
+ pc.filter(expected,
978
+ pc.or_(pc.or_(pc.match_substring(expected["thestring"], "abc"),
979
+ pc.match_substring(expected["thestring"], "xyz")),
980
+ pc.or_(pc.starts_with(expected["thestring"], "z"),
981
+ pc.is_null(expected["thestring"]))
982
+ )))
983
+ assert (t.select(predicate=((t["thestring"].contains("k")) & (t["theotherstring"].contains("h")) &
984
+ (t["theint"] > 500))).read_all() ==
985
+ pc.filter(expected, pc.and_(
986
+ pc.and_(pc.match_substring(expected["thestring"], "k"),
987
+ pc.match_substring(expected["theotherstring"], "h")),
988
+ pc.greater(expected["theint"], 500)
989
+ )))
990
+ for pred in failed_preds:
991
+ with pytest.raises(NotImplementedError):
992
+ t.select(predicate=pred(t)).read_all()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vastdb
3
- Version: 1.3.2
3
+ Version: 1.3.4
4
4
  Summary: VAST Data SDK
5
5
  Home-page: https://github.com/vast-data/vastdb_sdk
6
6
  Author: VAST DATA
@@ -138,26 +138,28 @@ vast_flatbuf/tabular/Column.py,sha256=2iXTlN8pynb_Sf7n15k8n2uXP_8eoz2RqKrUbTBrIy
138
138
  vast_flatbuf/tabular/ColumnType.py,sha256=_4-jMG08VR2zdn1ZH7F4aahYPxWsBSm7adUoVf-HFSU,151
139
139
  vast_flatbuf/tabular/CreateProjectionRequest.py,sha256=POlK1DrYMAldNJscLIRL3j4jAT0Sv_fRzfvBXwZAAMw,2516
140
140
  vast_flatbuf/tabular/CreateSchemaRequest.py,sha256=MrOfWaFu0Q1-mxLlGV8YMPajZ5kASyvowVSrKU-NPx8,1626
141
+ vast_flatbuf/tabular/CreateViewRequest.py,sha256=zagEVFrcWZLDY2exRHxr_uf-Yirchp7uYLk5b6Zvah8,4497
141
142
  vast_flatbuf/tabular/GetProjectionTableStatsResponse.py,sha256=Bp-ln-0lcZEiUvp3vWYmnCP6t2UsZ5J-lezgkUUWhzo,3474
142
143
  vast_flatbuf/tabular/GetTableStatsResponse.py,sha256=_UsKj6-VAvyDZ8Eku9fegQlRKV-T_0Dsb7qjulYoZus,4655
143
144
  vast_flatbuf/tabular/ImportDataRequest.py,sha256=f1chKp5d5NUxfNjI8YI1o4MYInF8UDhIhpWkT3vG4Do,2450
144
145
  vast_flatbuf/tabular/ListProjectionsResponse.py,sha256=secqrBsJY3ydbA28j09rmxzBqj-c1JNqaP7JMuib7nE,4240
145
146
  vast_flatbuf/tabular/ListSchemasResponse.py,sha256=V8tbwcWAC96eNwuoqDNqCSb02BnMdq60TpyISuWTVMk,3036
146
147
  vast_flatbuf/tabular/ListTablesResponse.py,sha256=V7jZAS8ryKY8s6o_QyjWzgan-rsGm17zjKEmi7K6qTM,3550
147
- vast_flatbuf/tabular/ObjectDetails.py,sha256=qW0WtbkCYYE_L-Kw6VNRDCLYaRm5lKvTbLNkfD4zV4A,3589
148
+ vast_flatbuf/tabular/ListViewsResponse.py,sha256=tn7mfW9afjWxMyBs3tsBcS_JA7uTAVNkFyaM7tctyns,3521
149
+ vast_flatbuf/tabular/ObjectDetails.py,sha256=yS16tYAu8BCYe7h_B4w7C9BDRVVxFMFg8_mkCyU7nOs,4697
148
150
  vast_flatbuf/tabular/S3File.py,sha256=KC9c2oS5-JXwTTriUVFdjOvRG0B54Cq9kviSDZY3NI0,4450
149
151
  vast_flatbuf/tabular/VipRange.py,sha256=_BJd1RRZAcK76T9vlsHzXKYVsPVaz6WTEAqStMQCAUQ,2069
150
152
  vast_flatbuf/tabular/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
151
153
  vastdb/__init__.py,sha256=J1JjKiFkKC95BHowfh9kJfQFTjRce-QMsc6zF_FfxC0,432
152
- vastdb/_internal.py,sha256=LmiiNmUqJFfNGUoe362sHwU8M3aKbN1VWYIvobrgeK8,92322
154
+ vastdb/_internal.py,sha256=asBizp21nQvYo1cE4Vs4OSH01s-5NyWZO2lb7_nP9oc,96538
153
155
  vastdb/bucket.py,sha256=5KuKhPjZOevznZqWHDVVocejvAy7dcwobPuV6BJCfPc,2544
154
156
  vastdb/config.py,sha256=1tMYtzKXerGcIUjH4tIGEvZNWvO4fviCEdcNCnELJZo,2269
155
157
  vastdb/conftest.py,sha256=X2kVveySPQYZlVBXUMoo7Oea5IsvmJzjdqq3fpH2kVw,3469
156
158
  vastdb/errors.py,sha256=2XR1ko7J5nkfiHSAgwuVAADw0SsyqxOwSeFaGgKZEXM,4186
157
159
  vastdb/features.py,sha256=DxV746LSkORwVSD6MP2hdXRfnyoLkJwtOwGmp1dnquo,1322
158
- vastdb/schema.py,sha256=EzTj0dIjLI23A2SAI1rYdhB6YqHdWSE4BBSqiKFbDtw,6418
160
+ vastdb/schema.py,sha256=N9GLEoSFPrbpreJbCwcWGCtknoNQkavqra8UQvCzy3E,6306
159
161
  vastdb/session.py,sha256=toMR0BXwTaECdWDKnIZky1F3MA1SmelRBiqCrqQ3GCM,2067
160
- vastdb/table.py,sha256=C6Zz0zolRRRbf5EQBvDRAofl3kGvfz4PjZwGeQongTI,31106
162
+ vastdb/table.py,sha256=PHtirFNL8j9PlXAgJp_rcLU37FgnWj0dPaFPFYWm28o,31186
161
163
  vastdb/transaction.py,sha256=NlVkEowJ_pmtffjWBBDaKExYDKPekjSZyj_fK_bZPJE,3026
162
164
  vastdb/util.py,sha256=8CUnVRsJukC3uNHNoB5D0qPf0FxS8OSdVB84nNoLJKc,6290
163
165
  vastdb/bench/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -189,21 +191,21 @@ vastdb/bench/perf_bench/query/query.py,sha256=h98Ui6vUTw16LHMY0ufkLnyVO3QCR8f1cX
189
191
  vastdb/bench/perf_bench/query/query_pyarrow.py,sha256=Dj5YPUvb4dAj7RskHfJcPijJnM-rdYIAItEF2dp4jfo,2305
190
192
  vastdb/bench/perf_bench/query/query_vastdb.py,sha256=SZYem_EmsaynEftAa_VFobjSJZDAcli9BckyRS3SFvg,2810
191
193
  vastdb/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
192
- vastdb/tests/metrics.py,sha256=N6ELJUmZubhAMmUtDbisXr6TFhSDgVCTTU05gBVxHRA,1010
194
+ vastdb/tests/metrics.py,sha256=ZCSeBYFSPMG3yI0JrAHs2CrY6wFjx_5GwRTYHVAwLKA,1026
193
195
  vastdb/tests/test_duckdb.py,sha256=STw_1PwTQR8Naz6s0p6lQTV1ZTKKhe3LPBUbhqzTCu0,1880
194
196
  vastdb/tests/test_imports.py,sha256=xKub3-bisFjH0BsZM8COfiUWuMrtoOoQKprF6VQT9RI,5669
195
197
  vastdb/tests/test_nested.py,sha256=LPU6uV3Ri23dBzAEMFQqRPbqapV5LfmiHSHkhILPIY0,6332
196
198
  vastdb/tests/test_projections.py,sha256=3y1kubwVrzO-xoR0hyps7zrjOJI8niCYspaFTN16Q9w,4540
197
- vastdb/tests/test_sanity.py,sha256=oiV2gb05aPyG5RMNUQZlyjNlg3T7Fig1_8OJzpAgcsk,3038
199
+ vastdb/tests/test_sanity.py,sha256=bv1ypGDzvOgmMvGbucDYiLQu8krQLlE6NB3M__q87x8,3303
198
200
  vastdb/tests/test_schemas.py,sha256=l70YQMlx2UL1KRQhApriiG2ZM7GJF-IzWU31H3Yqn1U,3312
199
- vastdb/tests/test_tables.py,sha256=17-t9VkEJRIW43Yf-lwEI7jHn8teOJvv-eZgANcvTkM,35023
201
+ vastdb/tests/test_tables.py,sha256=PwtUTmQdm4HDZJVq981h7RKcFDma2Loxz6FeQhQSu2Y,40101
200
202
  vastdb/tests/test_util.py,sha256=n7gvT5Wg6b6bxgqkFXkYqvFd_W1GlUdVfmPv66XYXyA,1956
201
203
  vastdb/tests/util.py,sha256=dpRJYbboDnlqL4qIdvScpp8--5fxRUBIcIYitrfcj9o,555
202
204
  vastdb/vast_tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
203
205
  vastdb/vast_tests/test_ha.py,sha256=744P4G6VJ09RIkHhMQL4wlipCBJWQVMhyvUrSc4k1HQ,975
204
206
  vastdb/vast_tests/test_scale.py,sha256=5jGwOdZH6Tv5tPdZYPWoqcxOceI2jA5i2D1zNKZHER4,3958
205
- vastdb-1.3.2.dist-info/LICENSE,sha256=obffan7LYrq7hLHNrY7vHcn2pKUTBUYXMKu-VOAvDxU,11333
206
- vastdb-1.3.2.dist-info/METADATA,sha256=NWJHQlPY-3V5nFmm9dLysGrLN7POvhVjq1qbFDfsr64,1340
207
- vastdb-1.3.2.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
208
- vastdb-1.3.2.dist-info/top_level.txt,sha256=Vsj2MKtlhPg0J4so64slQtnwjhgoPmJgcG-6YcVAwVc,20
209
- vastdb-1.3.2.dist-info/RECORD,,
207
+ vastdb-1.3.4.dist-info/LICENSE,sha256=obffan7LYrq7hLHNrY7vHcn2pKUTBUYXMKu-VOAvDxU,11333
208
+ vastdb-1.3.4.dist-info/METADATA,sha256=r-TcsDB09WBgIgLC-asWybRhn4pj0geXjD2zFVzveq0,1340
209
+ vastdb-1.3.4.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
210
+ vastdb-1.3.4.dist-info/top_level.txt,sha256=Vsj2MKtlhPg0J4so64slQtnwjhgoPmJgcG-6YcVAwVc,20
211
+ vastdb-1.3.4.dist-info/RECORD,,
File without changes