|
| 1 | +#!/usr/bin/env ruby |
| 2 | +# |
| 3 | +# Test to verify that 512KB LOBs can be fetched using both :long_as_string and :locator modes |
| 4 | +# |
| 5 | + |
| 6 | +require 'oci8' |
| 7 | +require_relative 'config' |
| 8 | + |
| 9 | +class TestLargeLob < Minitest::Test |
| 10 | + def setup |
| 11 | + @conn = get_oci8_connection |
| 12 | + @saved_lob_fetch_mode = OCI8.lob_fetch_mode |
| 13 | + drop_table('test_large_lob') |
| 14 | + @conn.exec(<<-SQL) |
| 15 | + CREATE TABLE test_large_lob ( |
| 16 | + id NUMBER, |
| 17 | + clob_data CLOB, |
| 18 | + blob_data BLOB |
| 19 | + ) |
| 20 | + SQL |
| 21 | + end |
| 22 | + |
| 23 | + def teardown |
| 24 | + return unless @conn |
| 25 | + |
| 26 | + OCI8.lob_fetch_mode = @saved_lob_fetch_mode |
| 27 | + drop_table('test_large_lob') |
| 28 | + @conn.logoff |
| 29 | + end |
| 30 | + |
| 31 | + def test_512kb_lob_roundtrip_with_long_interface |
| 32 | + size_kb = 512 |
| 33 | + clob_data = generate_text_data(size_kb) |
| 34 | + blob_data = generate_binary_data(size_kb) |
| 35 | + insert_lob_row(1, clob_data, blob_data) |
| 36 | + |
| 37 | + # Fetch using :long_as_string mode (LONG interface) |
| 38 | + OCI8.lob_fetch_mode = :long_as_string |
| 39 | + cursor = @conn.exec("SELECT id, clob_data, blob_data FROM test_large_lob WHERE id = 1") |
| 40 | + row = cursor.fetch |
| 41 | + cursor.close |
| 42 | + |
| 43 | + # Verify we got String objects with correct data |
| 44 | + assert_equal 1, row[0] |
| 45 | + assert_instance_of String, row[1], "CLOB should be fetched as String" |
| 46 | + assert_instance_of String, row[2], "BLOB should be fetched as String" |
| 47 | + assert_equal size_kb * 1024, row[1].size, "CLOB size should match" |
| 48 | + assert_equal size_kb * 1024, row[2].size, "BLOB size should match" |
| 49 | + assert_equal clob_data, row[1], "CLOB data should match (verifies chunk order)" |
| 50 | + assert_equal blob_data, row[2], "BLOB data should match (verifies chunk order)" |
| 51 | + end |
| 52 | + |
| 53 | + def test_512kb_lob_roundtrip_with_locator_mode |
| 54 | + size_kb = 512 |
| 55 | + clob_data = generate_text_data(size_kb) |
| 56 | + blob_data = generate_binary_data(size_kb) |
| 57 | + insert_lob_row(2, clob_data, blob_data) |
| 58 | + |
| 59 | + # Fetch using :locator mode (LOB locators) |
| 60 | + OCI8.lob_fetch_mode = :locator |
| 61 | + cursor = @conn.exec("SELECT id, clob_data, blob_data FROM test_large_lob WHERE id = 2") |
| 62 | + row = cursor.fetch |
| 63 | + cursor.close |
| 64 | + |
| 65 | + # Verify we got LOB locator objects |
| 66 | + assert_equal 2, row[0] |
| 67 | + assert_instance_of OCI8::CLOB, row[1], "CLOB should be fetched as locator" |
| 68 | + assert_instance_of OCI8::BLOB, row[2], "BLOB should be fetched as locator" |
| 69 | + |
| 70 | + # Read from locators and verify data |
| 71 | + fetched_clob_data = row[1].read |
| 72 | + fetched_blob_data = row[2].read |
| 73 | + assert_equal size_kb * 1024, fetched_clob_data.size, "CLOB size should match" |
| 74 | + assert_equal size_kb * 1024, fetched_blob_data.size, "BLOB size should match" |
| 75 | + assert_equal clob_data, fetched_clob_data, "CLOB data should match (verifies chunk order)" |
| 76 | + assert_equal blob_data, fetched_blob_data, "BLOB data should match (verifies chunk order)" |
| 77 | + end |
| 78 | + |
| 79 | + private |
| 80 | + |
| 81 | + def generate_text_data(size_kb, seed = 42) |
| 82 | + # hex encoding: 1 byte -> 2 hex characters |
| 83 | + binary_size_kb = (size_kb / 2.0).ceil |
| 84 | + binary_data = generate_binary_data(binary_size_kb, seed) |
| 85 | + hex_data = binary_data.unpack1('H*') |
| 86 | + hex_data[0, size_bytes] # truncate to exact requested size |
| 87 | + end |
| 88 | + |
| 89 | + def generate_binary_data(size_kb, seed = 42) |
| 90 | + Random.new(seed).bytes(size_kb * 1024) |
| 91 | + end |
| 92 | + |
| 93 | + def insert_lob_row(id, clob_data, blob_data) |
| 94 | + cursor = @conn.parse("INSERT INTO test_large_lob VALUES (:1, :2, :3)") |
| 95 | + cursor.exec(id, OCI8::CLOB.new(@conn, clob_data), OCI8::BLOB.new(@conn, blob_data)) |
| 96 | + cursor.close |
| 97 | + @conn.commit |
| 98 | + end |
| 99 | +end |
0 commit comments