mirror of
https://github.com/lancedb/lancedb.git
synced 2026-10-27 14:06:14 +00:00
fix!: normalize blob layouts and support external byte ranges (#4589)
This commit is contained in:
@@ -3254,6 +3254,89 @@ BLOB_DESCRIBE_RESPONSE = {
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("use_async", [False, True])
|
||||
@pytest.mark.parametrize("mode", ["merge", "overwrite"])
|
||||
@pytest.mark.parametrize("target_with_ranges", [False, True])
|
||||
async def test_remote_writes_normalize_blob_layouts(
|
||||
use_async, mode, target_with_ranges
|
||||
):
|
||||
description = json.loads(json.dumps(BLOB_DESCRIBE_RESPONSE))
|
||||
if target_with_ranges:
|
||||
description["schema"]["fields"][1]["type"]["fields"].extend(
|
||||
{"name": name, "type": {"type": "uint64"}, "nullable": True}
|
||||
for name in ("position", "size")
|
||||
)
|
||||
received = []
|
||||
|
||||
def handler(request):
|
||||
if request.path == "/v1/table/test/describe/":
|
||||
response = description
|
||||
else:
|
||||
expected = "merge_insert" if mode == "merge" else "insert"
|
||||
assert urlparse(request.path).path == f"/v1/table/test/{expected}/"
|
||||
if request.headers.get("Transfer-Encoding") == "chunked":
|
||||
body = bytearray()
|
||||
while size := int(request.rfile.readline().split(b";")[0], 16):
|
||||
body.extend(request.rfile.read(size))
|
||||
assert request.rfile.read(2) == b"\r\n"
|
||||
assert request.rfile.read(2) == b"\r\n"
|
||||
else:
|
||||
body = request.rfile.read(int(request.headers["Content-Length"]))
|
||||
received.append(pa.ipc.open_stream(body).read_all())
|
||||
response = {
|
||||
"version": 2,
|
||||
"num_updated_rows": 0,
|
||||
"num_inserted_rows": 2,
|
||||
"num_deleted_rows": 0,
|
||||
}
|
||||
request.send_response(200)
|
||||
request.send_header("Content-Type", "application/json")
|
||||
request.send_header("phalanx-version", "0.5.0")
|
||||
request.end_headers()
|
||||
request.wfile.write(json.dumps(response).encode())
|
||||
|
||||
field = lancedb.blob("image")
|
||||
storage_type = field.type.storage_type
|
||||
if target_with_ranges:
|
||||
storage_type = pa.struct(list(storage_type)[:2])
|
||||
field = pa.field(
|
||||
"image",
|
||||
storage_type,
|
||||
metadata={"ARROW:extension:name": "lance.blob.v2"},
|
||||
)
|
||||
images = pa.array([{"data": b"payload"}, None], type=storage_type)
|
||||
if isinstance(field.type, pa.ExtensionType):
|
||||
images = pa.ExtensionArray.from_storage(field.type, images)
|
||||
data = pa.Table.from_arrays(
|
||||
[pa.array([1, 2], type=pa.int64()), images],
|
||||
schema=pa.schema([pa.field("id", pa.int64()), field]),
|
||||
)
|
||||
|
||||
with mock_lancedb_connection(handler) as db:
|
||||
remote_table = db.open_table("test")
|
||||
table = remote_table._table if use_async else remote_table
|
||||
if mode == "merge":
|
||||
result = (
|
||||
table.merge_insert("id").when_not_matched_insert_all().execute(data)
|
||||
)
|
||||
else:
|
||||
result = table.add(data, mode="overwrite")
|
||||
if use_async:
|
||||
await result
|
||||
|
||||
assert len(received) == 1
|
||||
images = received[0]["image"].combine_chunks()
|
||||
if isinstance(images.type, pa.ExtensionType):
|
||||
images = images.storage
|
||||
with_ranges = target_with_ranges if mode == "merge" else not target_with_ranges
|
||||
assert images.type.num_fields == (4 if with_ranges else 2)
|
||||
expected = {"data": b"payload", "uri": None}
|
||||
if with_ranges:
|
||||
expected.update(position=None, size=None)
|
||||
assert images.to_pylist() == [expected, None]
|
||||
|
||||
|
||||
def blob_query_response_table():
|
||||
image_field = pa.field(
|
||||
"image",
|
||||
|
||||
Reference in New Issue
Block a user