PostgreSQLのbytea型を操る!set_byte関数徹底解説
PostgreSQLでは、バイナリデータを格納するために bytea
というデータ型が提供されています。これは、任意のバイト列(バイナリ文字列)を扱うことができます。通常のテキスト文字列とは異なり、bytea
はNULLバイト (\0
) や非表示文字などもそのまま格納できます。
set_byte
関数は、この bytea
型のデータに対して、特定のバイトを上書き(設定)する ために使用されます。
set_byte関数の構文
set_byte(bytes bytea, n integer, newvalue integer) → bytea
newvalue
: その位置に設定したい新しいバイトの値。これは0から255までの整数(符号なし8ビット整数)で指定します。n
: 設定したいバイトの位置(オフセット)。0から始まるインデックス です。つまり、最初のバイトは0、次のバイトは1となります。bytes
: 変更したいbytea
型のバイナリ文字列。
set_byte関数の機能
この関数は、指定された bytea
型の bytes
の n
番目の位置にあるバイトを、newvalue
で指定された値に変更します。変更後の新しい bytea
値を返します。
例
元のバイナリ文字列が \x1234567890
だとします。
(\x
は16進数表現であることを示し、12
が1バイト目、34
が2バイト目...となります。)
SELECT set_byte('\x1234567890'::bytea, 4, 64);
この例では、以下のように動作します。
'\x1234567890'::bytea
: 対象となるバイナリ文字列です。4
: 設定したいバイトの位置。0から数えるので、これは5番目のバイトを指します。- 0番目:
12
- 1番目:
34
- 2番目:
56
- 3番目:
78
- 4番目:
90
- 0番目:
64
: 5番目のバイトを64
(10進数) に変更します。16進数で64
は40
に相当します。
結果は以下のようになります。
set_byte
------------
\x1234567840
(1 row)
元の \x1234567890
の5番目のバイト 90
が 40
に変更されています。
用途
set_byte
関数は、バイナリデータの一部を直接操作したい場合に非常に便利です。例えば、
- 暗号化されたデータの一部を特定のアルゴリズムで変更する場合(ただし、慎重な設計が必要)。
- 独自のバイナリフォーマットのデータを扱い、ヘッダー情報や特定のフラグバイトを更新したい場合。
- ファイルの内容を
bytea
型でデータベースに格納し、その特定の部分を修正したい場合。
など、低レベルなバイト操作が必要なシナリオで利用されます。
newvalue
は0から255の範囲である必要があります。範囲外の値を指定すると、挙動が予期せぬものになるか、エラーになる可能性があります。n
で指定するオフセットが、元のbytea
の長さを超える場合、エラーが発生します。
エラー: ERROR: offset out of bounds (オフセットが範囲外)
原因
set_byte(bytes bytea, n integer, newvalue integer)
関数において、n
で指定したオフセット(位置)が、bytes
の実際の長さよりも大きい場合、このエラーが発生します。インデックスは0から始まるため、長さがLのバイト列の場合、有効なオフセットは0からL-1までです。
例
長さが5バイトの bytea
に対して、オフセット5(6番目のバイト)を設定しようとした場合。
SELECT set_byte('\x1234567890'::bytea, 5, 255);
-- エラー: offset out of bounds
トラブルシューティング
- overlay() の検討
バイナリデータの中央に新しいバイト列を挿入したり、既存のバイト列を別のバイト列で置き換えたりする場合は、set_byte
よりもoverlay()
関数の方が適している場合があります。overlay()
は、指定したオフセットから指定した長さの範囲を置き換えることができます。 - オフセットの調整
設定したいバイトが実際に存在する位置であることを確認し、n
の値を修正してください。 - オフセットの確認
bytea
の長さ(length()
関数で取得可能)と、設定しようとしているオフセットn
を確認してください。n
はlength(bytes) - 1
以下である必要があります。SELECT length('\x1234567890'::bytea); -- 結果: 5 -- オフセットは0から4までが有効
エラー: ERROR: integer out of range または newvalue の値が不正
原因
set_byte
の第3引数 newvalue
は、0から255までの整数(符号なし8ビット整数)で指定する必要があります。この範囲外の値を指定した場合、エラーが発生する可能性があります。
例
SELECT set_byte('\x12'::bytea, 0, 300);
-- エラー: integer out of range for type bytea
トラブルシューティング
- データ型の確認
newvalue
の型が適切に整数として扱われているか確認してください。他のデータ型から変換する場合、意図しない値になることがあります。 - 値の範囲確認
newvalue
が0 <= newvalue <= 255
の範囲内にあることを確認してください。
意図しないバイト値への変換
原因
特にプログラミング言語からSQLを実行する場合に発生しがちです。整数値を set_byte
に渡す際に、データ型やエンコーディングの取り扱いの違いにより、期待した値とは異なるバイト値が設定されてしまうことがあります。
例
例えば、ある言語で負の数をバイトとして扱おうとしたり、UTF-8エンコードされた文字をそのまま整数に変換して設定しようとしたりした場合。
トラブルシューティング
- プログラムからの呼び出し
アプリケーションコードでset_byte
を呼び出す場合、渡す引数の型がPostgreSQLの期待する型(bytea
,integer
,integer
)と一致しているか確認してください。JDBCやpsycopg2などのドライバが正しく型変換を行っているか、または明示的なキャストが必要ないかを確認します。 - エンコーディングの理解
bytea
は生バイトデータを格納するため、文字エンコーディングの影響を受けません。しかし、もしテキストデータをbytea
に変換してからset_byte
を使っている場合は、元のテキストのエンコーディングとbytea
の内容の関係を正確に理解する必要があります。 - 値の正確な指定
newvalue
として設定する整数値が、そのバイトの正確な10進数表現であることを確認してください。16進数で表現する場合は、\x
プレフィックスを使用すると分かりやすいです。- 例: 10進数の64は16進数で
0x40
なので、set_byte(..., 64)
またはset_byte(..., '\x40'::integer)
のように指定します。
- 例: 10進数の64は16進数で
bytea データ型の表現形式に関する混乱
原因
PostgreSQLは bytea
型の値を、クライアントの bytea_output
設定に応じて異なる形式で表示します(例: hex
または escape
)。これにより、結果が期待通りに見えないことがあります。
例
set_byte
を使ってバイトを設定したのに、SELECT
で表示される結果が \x
で始まらず、エスケープシーケンスになっている場合。
トラブルシューティング
- hex 形式の使用を推奨
クエリで16進数リテラルを使用する場合は、E'\\x...'
のようにE
プレフィックスを付けてエスケープ文字列として扱うか、'\x...'::bytea
のように明示的にキャストすることで、誤解を避けることができます。 - bytea_output 設定の確認
通常はSHOW bytea_output;
hex
に設定されていることが多いですが、escape
になっている場合は、表示が\001
や\x01
のようにエスケープされた形式になります。これは表示形式の問題であり、内部データが壊れているわけではありません。
bytea の不変性に関する誤解
原因
set_byte
は元の bytea
を直接変更するのではなく、新しい bytea
値を返します。そのため、UPDATE
文で列を更新しない限り、データベースに格納された値は変わりません。
例
-- テーブルがあると仮定
-- CREATE TABLE my_binary_data (id serial PRIMARY KEY, data bytea);
-- INSERT INTO my_binary_data (data) VALUES ('\x0102030405');
SELECT set_byte(data, 0, 255) FROM my_binary_data WHERE id = 1;
-- このSELECT文は変更された新しいbyteaを返すだけで、テーブルのデータは変更しない
-
UPDATE 文の使用
データベース内のデータを変更したい場合は、必ずUPDATE
文の中でset_byte
を使用してください。UPDATE my_binary_data SET data = set_byte(data, 0, 255) WHERE id = 1;
どの言語でも、基本的な流れは以下のようになります。
- PostgreSQL への接続
各言語のデータベースドライバを使ってPostgreSQLデータベースに接続します。 - bytea 型データの準備
データベースからbytea
型のデータを取得するか、プログラム内で新しいbytea
データを作成します。 - set_byte の実行
SQLクエリとしてset_byte
関数を呼び出し、特定のバイトを更新します。 - 結果の処理
更新されたデータが返される場合、それを処理します。データベースのデータを永続的に更新したい場合はUPDATE
文を使います。
事前準備 (共通)
まず、bytea
型のデータを格納するテーブルを作成しておきます。
CREATE TABLE binary_data_table (
id SERIAL PRIMARY KEY,
data BYTEA
);
-- サンプルデータを挿入
INSERT INTO binary_data_table (data) VALUES ('\x0102030405060708090A');
Python (psycopg2)
PythonでPostgreSQLに接続するには、psycopg2
ライブラリがよく使われます。
import psycopg2
from psycopg2.extensions import Binary
# データベース接続情報
DB_HOST = "localhost"
DB_NAME = "your_database_name"
DB_USER = "your_username"
DB_PASSWORD = "your_password"
try:
# データベースに接続
conn = psycopg2.connect(host=DB_HOST, database=DB_NAME, user=DB_USER, password=DB_PASSWORD)
cur = conn.cursor()
# 1. 既存のbyteaデータを取得
record_id = 1
cur.execute("SELECT data FROM binary_data_table WHERE id = %s", (record_id,))
original_data = cur.fetchone()[0]
print(f"Original data (hex): {original_data.hex()}") # byteaはbytesオブジェクトとして取得される
# 2. set_byteを使って特定のバイトを更新
# 3番目のバイト(インデックス2)を10進数の255(16進数でFF)に設定
offset_to_set = 2
new_byte_value = 255 # 0-255 の範囲
# SQLのset_byte関数を呼び出す
# 注意: set_byteは新しいbyteaを返すので、UPDATE文で更新する必要がある
update_query = """
UPDATE binary_data_table
SET data = set_byte(data, %s, %s)
WHERE id = %s
RETURNING data; -- 更新後のデータを取得
"""
cur.execute(update_query, (offset_to_set, new_byte_value, record_id))
updated_data = cur.fetchone()[0]
conn.commit() # 変更をコミット
print(f"Updated data (hex): {updated_data.hex()}")
# 新しいbyteaデータを作成し、set_byteを適用する例
new_binary_string = b'\xAA\xBB\xCC\xDD'
# 1番目のバイト(インデックス0)を0に設定
temp_updated_binary = cur.execute("SELECT set_byte(%s, %s, %s)", (Binary(new_binary_string), 0, 0))
# Python側でbytesオブジェクトを構築し、それに対して操作を行う場合は、
# Pythonのbytesオブジェクトの変更は直接行えないため、スライスなどを使う必要があります。
# しかし、PostgreSQLのset_byteはSQLレベルで操作を行うため、bytesオブジェクトをそのまま渡して利用できます。
except psycopg2.Error as e:
print(f"Database error: {e}")
finally:
if conn:
cur.close()
conn.close()
Pythonでのポイント
- データベースのデータを永続的に変更するには、
UPDATE ... SET data = set_byte(...)
の形式を使います。 set_byte
はSQL関数なので、それを呼び出すSQLクエリを構築します。bytes.hex()
メソッドを使うと、バイナリデータを16進数文字列で表示できます。psycopg2
はbytea
型をPythonのbytes
型として扱います。
Node.js (pg)
Node.jsでPostgreSQLに接続するには、pg
(node-postgres) ライブラリがよく使われます。
const { Pool } = require('pg');
// データベース接続情報
const pool = new Pool({
user: 'your_username',
host: 'localhost',
database: 'your_database_name',
password: 'your_password',
port: 5432,
});
async function updateBinaryData() {
let client;
try {
client = await pool.connect();
const recordId = 1;
// 1. 既存のbyteaデータを取得
const resOriginal = await client.query('SELECT data FROM binary_data_table WHERE id = $1', [recordId]);
const originalDataBuffer = resOriginal.rows[0].data; // byteaはBufferオブジェクトとして取得される
console.log(`Original data (hex): ${originalDataBuffer.toString('hex')}`);
// 2. set_byteを使って特定のバイトを更新
// 3番目のバイト(インデックス2)を10進数の255(16進数でFF)に設定
const offsetToSet = 2;
const newByteValue = 255; // 0-255 の範囲
const updateQuery = `
UPDATE binary_data_table
SET data = set_byte(data, $1, $2)
WHERE id = $3
RETURNING data; -- 更新後のデータを取得
`;
const resUpdated = await client.query(updateQuery, [offsetToSet, newByteValue, recordId]);
const updatedDataBuffer = resUpdated.rows[0].data;
console.log(`Updated data (hex): ${updatedDataBuffer.toString('hex')}`);
// 新しいbyteaデータを作成し、set_byteを適用する例
const newBinaryString = Buffer.from([0xAA, 0xBB, 0xCC, 0xDD]); // Bufferを直接渡す
const tempRes = await client.query('SELECT set_byte($1, $2, $3)', [newBinaryString, 0, 0]);
const tempUpdatedBinary = tempRes.rows[0].set_byte; // 関数結果は列名set_byteで返される
console.log(`Temp updated binary (hex): ${tempUpdatedBinary.toString('hex')}`);
} catch (err) {
console.error('Database error:', err);
} finally {
if (client) {
client.release();
}
}
}
updateBinaryData();
Node.jsでのポイント
set_byte
の引数にBuffer
を直接渡すことができます。Buffer.toString('hex')
で16進数文字列に変換できます。pg
はbytea
型をNode.jsのBuffer
オブジェクトとして扱います。
Java (JDBC)
JavaでPostgreSQLに接続するには、PostgreSQL JDBC Driver を使用します。MavenやGradleを使って依存関係に追加します。
Maven の場合
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version> </dependency>
Java コード例
import java.sql.*;
public class SetByteExample {
// データベース接続情報
private static final String DB_URL = "jdbc:postgresql://localhost:5432/your_database_name";
private static final String DB_USER = "your_username";
private static final String DB_PASSWORD = "your_password";
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// データベースに接続
conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASSWORD);
System.out.println("データベースに接続しました。");
int recordId = 1;
// 1. 既存のbyteaデータを取得
String selectSql = "SELECT data FROM binary_data_table WHERE id = ?";
pstmt = conn.prepareStatement(selectSql);
pstmt.setInt(1, recordId);
rs = pstmt.executeQuery();
byte[] originalData = null;
if (rs.next()) {
originalData = rs.getBytes("data");
System.out.println("Original data (hex): " + bytesToHex(originalData));
}
rs.close();
pstmt.close();
// 2. set_byteを使って特定のバイトを更新
// 3番目のバイト(インデックス2)を10進数の255(16進数でFF)に設定
int offsetToSet = 2;
int newByteValue = 255; // 0-255 の範囲
String updateSql = "UPDATE binary_data_table SET data = set_byte(data, ?, ?) WHERE id = ? RETURNING data";
pstmt = conn.prepareStatement(updateSql);
pstmt.setInt(1, offsetToSet);
pstmt.setInt(2, newByteValue);
pstmt.setInt(3, recordId);
rs = pstmt.executeQuery();
byte[] updatedData = null;
if (rs.next()) {
updatedData = rs.getBytes("data");
System.out.println("Updated data (hex): " + bytesToHex(updatedData));
}
rs.close();
pstmt.close();
// 新しいbyteaデータを作成し、set_byteを適用する例
byte[] newBinaryString = new byte[]{(byte) 0xAA, (byte) 0xBB, (byte) 0xCC, (byte) 0xDD};
String tempSetByteSql = "SELECT set_byte(?, ?, ?)";
pstmt = conn.prepareStatement(tempSetByteSql);
pstmt.setBytes(1, newBinaryString); // byte[]を直接渡す
pstmt.setInt(2, 0); // インデックス0
pstmt.setInt(3, 0); // 値を0に設定
rs = pstmt.executeQuery();
byte[] tempUpdatedBinary = null;
if (rs.next()) {
tempUpdatedBinary = rs.getBytes(1); // 関数結果は1列目として取得
System.out.println("Temp updated binary (hex): " + bytesToHex(tempUpdatedBinary));
}
} catch (SQLException e) {
System.err.println("データベースエラーが発生しました: " + e.getMessage());
e.printStackTrace();
} finally {
try {
if (rs != null) rs.close();
if (pstmt != null) pstmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
System.err.println("リソースのクローズに失敗しました: " + e.getMessage());
}
}
}
// byte[]を16進数文字列に変換するヘルパーメソッド
private static String bytesToHex(byte[] bytes) {
if (bytes == null) {
return "null";
}
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X", b));
}
return sb.toString();
}
}
- Javaでバイトの16進数表現を表示するために、
bytesToHex
のようなヘルパーメソッドが必要になることがあります。 getBytes()
メソッドでbyte[]
を取得します。PreparedStatement
のsetBytes()
メソッドでbyte[]
を渡すことができます。- JDBCでは
bytea
型はJavaのbyte[]
として扱われます。
PostgreSQL set_byte
の代替プログラミング方法
大きく分けて、以下の2つのアプローチがあります。
- PostgreSQL の組み込み関数を利用する
- アプリケーション側でバイナリデータを操作する
PostgreSQL の組み込み関数を利用する代替方法
set_byte
が単一バイトの更新に特化しているのに対し、PostgreSQLにはより汎用的なバイナリ操作関数がいくつか用意されています。
a. overlay()
関数
overlay()
は、bytea
の特定の部分を別の bytea
で置き換えることができる、非常に強力な関数です。単一バイトの置き換えだけでなく、複数のバイトや、挿入・削除のような操作にも使えます。
構文
overlay(string bytea, newsubstring bytea, start integer [, length integer]) → bytea
length
: (オプション) 置き換える元のstring
の長さ。指定しない場合、newsubstring
の長さが使われます。start
: 置き換えを開始するオフセット(1から始まるインデックス)。newsubstring
: 置き換える新しいbytea
。string
: 元のbytea
。
set_byte との比較
- 新しいバイト列を挿入したい場合は
overlay(original, new_bytes, start)
のようにlength
を省略します。 overlay()
はより汎用的で、複数のバイトを一度に置き換えることができます。set_byte
でset_byte(my_bytea, 2, 255)
とする場合、overlay
ではoverlay(my_bytea, E'\\xff'::bytea, 3, 1)
となります。(インデックスが0始まりか1始まりかの違いと、値がbytea
リテラルである必要がある点に注意)
例
3番目のバイト(インデックス2)を 0xFF
に変更(set_byte
と同じ操作)
SELECT overlay('\x0102030405'::bytea, '\xFF'::bytea, 3, 1);
-- 結果: \x0102ff0405
特定のオフセットに新しいバイト列を挿入(set_byte
ではできない操作)
SELECT overlay('\x0102030405'::bytea, '\xAAFF'::bytea, 3);
-- 結果: \x0102aaff030405 (3番目の位置にAASが入って、残りがずれている)
利点
- より複雑なバイト列の置換操作に対応できます。
- PostgreSQLサーバー側で処理が完結するため、クライアントとのデータ転送量が少ない場合があります。
欠点
- インデックスが1始まりであるため、0始まりに慣れている場合は混乱する可能性があります。
- 単一バイトの置換のためだけに使うと、
set_byte
より記述が長くなります。
b. set_bytea_byte()
関数 (PostgreSQL 9.0 以降)
これは set_byte
の別名(エイリアス)であり、機能は全く同じです。
set_bytea_byte(bytes bytea, n integer, newvalue integer) → bytea
これは代替というよりは同義語ですが、コードをレビューする際に知っておくと役立ちます。
c. 部分的な抽出と連結 (substring()
と ||
演算子)
これは、最も基本的な(しかし手作業が多い)方法です。元の bytea
を変更したい位置で3つの部分に分割し、間に新しいバイトを挟んで再連結します。
例
3番目のバイト(インデックス2)を 0xFF
に変更
SELECT
substring('\x0102030405'::bytea FROM 1 FOR 2) || -- 最初の2バイト (0x0102)
'\xFF'::bytea || -- 新しいバイト (0xFF)
substring('\x0102030405'::bytea FROM 4); -- 残りのバイト (0x0405)
-- 結果: \x0102ff0405
利点
overlay()
が利用できない古いPostgreSQLバージョンでも機能します(ただし、bytea
版substring
は8.3以降)。- 概念が非常にシンプルで、SQLの文字列操作の知識があれば理解しやすいです。
欠点
- インデックスの計算が複雑になりがちです。
- パフォーマンスが他の関数に比べて劣る可能性があります(特に大きなバイナリデータの場合)。
- 非常に冗長で、コードが読みにくくなります。
アプリケーション側でバイナリデータを操作する代替方法
これは、PostgreSQLから bytea
データを取得し、アプリケーション(Python, Node.js, Javaなど)のメモリ上でそのデータを操作し、変更されたデータをデータベースに書き戻すアプローチです。
一般的な手順
- データ取得
SELECT data FROM my_table WHERE id = ?
のようにして、bytea
データを取得します。 - データ変換
取得したbytea
を、各言語で扱いやすいバイナリデータ型(Pythonのbytes
/bytearray
、Node.jsのBuffer
、Javaのbyte[]
など)に変換します。 - バイト操作
各言語の機能を使って、目的のオフセットのバイトを直接変更します。- Python
bytearray
は可変なので、my_bytearray[offset] = new_value
のように直接変更できます。bytes
は不変なので、スライスと連結で新しいbytes
オブジェクトを作成する必要があります。 - Node.js
Buffer
は可変なので、myBuffer[offset] = new_value
のように直接変更できます。 - Java
byte[]
は可変なので、myByteArray[offset] = (byte)new_value
のように直接変更できます。
- Python
- データ書き戻し
変更したバイナリデータをUPDATE my_table SET data = ? WHERE id = ?
のようにしてデータベースに書き戻します。
Python の例 (bytearray を使用)
import psycopg2
# ... (データベース接続コードは省略) ...
try:
cur = conn.cursor()
record_id = 1
cur.execute("SELECT data FROM binary_data_table WHERE id = %s", (record_id,))
original_bytes = cur.fetchone()[0] # bytes型で取得
print(f"Original data (hex): {original_bytes.hex()}")
# bytesをbytearrayに変換して、可変にする
mutable_data = bytearray(original_bytes)
# 3番目のバイト(インデックス2)を255に設定
offset_to_set = 2
new_byte_value = 255
if offset_to_set < len(mutable_data):
mutable_data[offset_to_set] = new_byte_value
print(f"Modified in app (hex): {mutable_data.hex()}")
# 変更されたbytearrayをデータベースに書き戻す
update_query = "UPDATE binary_data_table SET data = %s WHERE id = %s;"
cur.execute(update_query, (bytes(mutable_data), record_id)) # bytearrayをbytesに戻して渡す
conn.commit()
print("Data updated in database via app-side manipulation.")
else:
print("Offset out of bounds for app-side manipulation.")
except psycopg2.Error as e:
print(f"Database error: {e}")
finally:
if conn:
cur.close()
conn.close()
利点
- 特定のユースケース
データがすでにアプリケーションにロードされている場合や、複数の操作を一括で行う場合に効率的です。 - デバッグの容易さ
アプリケーションコード内でステップ実行してデバッグできます。 - 柔軟性
アプリケーション側の言語の豊富なデータ構造とアルゴリズムを利用して、より複雑なバイナリ処理(ビット操作、構造体の解析、複合的な検証など)を行うことができます。
- コードの複雑さ
SQLレベルで完結する操作が、アプリケーションレベルで複数のステップに分割され、コードが複雑になることがあります。 - 同期の問題
複数のクライアントが同じデータを同時に変更しようとすると、競合状態(race condition)が発生しやすくなります。適切なロックやトランザクション管理が必要です。 - メモリ消費
大量のbytea
データをメモリにロードする必要があるため、アプリケーションのメモリ消費が増えます。 - ネットワークオーバーヘッド
大量のbytea
データをサーバーからクライアントへ、そしてまたクライアントからサーバーへ転送する必要があるため、ネットワーク帯域とレイテンシの消費が大きくなります。
- 同時実行性(concurrency)が懸念される場合
- PostgreSQLの組み込み関数はトランザクション内で自動的に処理されるため、競合管理がしやすい場合があります。アプリケーション側で操作する場合は、明示的なロックや悲観的/楽観的ロック戦略の導入が必要になることがあります。
- 複雑なバイナリ構造の解析や、複数の論理的な操作を一度に行う必要がある場合
- アプリケーション側で操作する方が柔軟性が高く、デバッグもしやすいでしょう。ただし、パフォーマンスとネットワークコストを考慮する必要があります。
- データが大きく、ネットワーク転送量を最小限に抑えたい場合
- PostgreSQLの組み込み関数(
set_byte
,overlay
,substring
)が適しています。
- PostgreSQLの組み込み関数(
- 単一のバイトをサーバー側で効率的に変更したい場合
set_byte()
が最もシンプルで直接的です。- より汎用的なバイト列置換や挿入が必要な場合は
overlay()
。