วิธีแทรกข้อมูล (ไฟล์) ลงในคอลัมน์ PostgreSQL bytea ได้อย่างไร


37

คำถามนี้ไม่เกี่ยวกับ bytea v. oid v. blobs v. วัตถุขนาดใหญ่ ฯลฯ

ฉันมีตารางที่ประกอบด้วยintegerเขตข้อมูลคีย์หลักและbyteaเขตข้อมูล ฉันต้องการป้อนข้อมูลลงในbyteaช่อง สิ่งนี้สามารถสันนิษฐานได้ว่าทำได้ด้วยPL/ภาษาใดภาษาหนึ่งและฉันอาจจะลองทำสิ่งนี้PL/Pythonในอนาคต

ขณะที่ฉันยังคงทดสอบและทดลองใช้ฉันต้องการแทรกข้อมูลจากไฟล์ (บนเซิร์ฟเวอร์) โดยใช้คำสั่ง SQL "มาตรฐาน" ฉันทราบว่าผู้ดูแลระบบที่มีสิทธิ์เขียนบนเซิร์ฟเวอร์เท่านั้นที่จะสามารถแทรกข้อมูลในแบบที่ฉันต้องการ ฉันไม่ได้กังวลเกี่ยวกับเรื่องนี้ในขณะนี้เนื่องจากผู้ใช้จะไม่สามารถแทรกbyteaข้อมูลในปัจจุบัน ฉันได้ค้นหาไซต์ StackExchange ต่างๆ, คลังข้อมูล PostgreSQL และอินเทอร์เน็ตโดยทั่วไปแล้ว แต่ไม่สามารถหาคำตอบได้

แก้ไข: นี้การอภิปรายจาก 2008 แสดงให้เห็นว่าสิ่งที่ฉันต้องการจะทำคือไปไม่ได้ มีการbyteaใช้ฟิลด์อย่างไร

แก้ไข: คำถามที่คล้ายกันนี้จากปี 2005 ยังไม่มีคำตอบ

แก้ไขได้:รายละเอียดที่ให้ไว้ที่นี่ในpsycopgเว็บไซต์เป็นพื้นฐานสำหรับการแก้ปัญหาที่ฉันเขียนใน Python มันก็อาจจะเป็นไปได้ที่จะใส่ข้อมูลไบนารีเป็นคอลัมน์ที่ใช้bytea PL/Pythonฉันไม่ทราบว่าสิ่งนี้เป็นไปได้โดยใช้ "บริสุทธิ์" SQL


1
ลิงก์ไปยัง psycopg docs ใช้งานไม่ได้และการแก้ไขของฉันดูเหมือนจะถูกปฏิเสธ (!?) นี่คือที่ตั้งปัจจุบัน
Aryeh Leib Taurog

@AryehLeibTaurog: ขอบคุณ ฉันปฏิเสธการแก้ไขเนื่องจากมันไม่ชัดเจนสำหรับฉันว่าข้อความที่คุณเปลี่ยนแปลงนั้นเป็นการเชื่อมโยงหลายมิติ หากคุณต้องการแก้ไขอีกครั้งฉันจะอนุมัติ
SabreWolfy

@Andriy_M ทำไมคุณถึงคิดว่า "การแก้ไขนี้เบี่ยงเบนไปจากความตั้งใจดั้งเดิมของโพสต์" (การแก้ไขทำโดย informatik01?)
miracle173

@ miracle173: เพราะฉันได้รับความประทับใจว่าแท็กที่แนะนำบางรายการไม่เกี่ยวข้อง (ดีจริง ๆ แค่แท็กเดียวblob) หากนั่นเป็นข้อผิดพลาดฉันขอโทษอย่างจริงใจ
Andriy M

คำตอบ:


26

ในฐานะ superuser:

create or replace function bytea_import(p_path text, p_result out bytea) 
                   language plpgsql as $$
declare
  l_oid oid;
begin
  select lo_import(p_path) into l_oid;
  select lo_get(l_oid) INTO p_result;
  perform lo_unlink(l_oid);
end;$$;

lo_get เปิดตัวใน 9.4 ดังนั้นสำหรับรุ่นเก่าที่คุณต้องการ:

create or replace function bytea_import(p_path text, p_result out bytea) 
                   language plpgsql as $$
declare
  l_oid oid;
  r record;
begin
  p_result := '';
  select lo_import(p_path) into l_oid;
  for r in ( select data 
             from pg_largeobject 
             where loid = l_oid 
             order by pageno ) loop
    p_result = p_result || r.data;
  end loop;
  perform lo_unlink(l_oid);
end;$$;

แล้ว:

insert into my_table(bytea_data) select bytea_import('/my/file.name');

สำหรับกระบวนการย้อนกลับฉันไม่ได้ลองสิ่งนี้แต่ถ้าได้ผลlo_exportจะเป็นทุกอย่างที่คุณต้องการ
Jack Douglas


15

การแก้ปัญหานี้ไม่ได้มีประสิทธิภาพว่าในแง่ของการรันไทม์ แต่มันนิด ๆ COPY BINARYง่ายเมื่อเทียบกับการทำส่วนหัวของคุณเอง นอกจากนี้ไม่จำเป็นต้องใช้ไลบรารีหรือภาษาสคริปต์นอกเหนือจากการทุบตี

ก่อนอื่นให้แปลงไฟล์เป็น hexdump เพิ่มขนาดไฟล์เป็นสองเท่า xxd -pทำให้เราใกล้ชิดกันมาก แต่มันก็เกิดขึ้นในบางบรรทัดใหม่ที่น่ารำคาญที่เราต้องดูแล:

xxd -p /path/file.bin | tr -d '\n' > /path/file.hex

ถัดไปนำเข้าข้อมูลใน PostgreSQL เป็นtextฟิลด์ที่มีขนาดใหญ่มาก ประเภทนี้เก็บได้มากถึงหนึ่ง GB ต่อค่าฟิลด์ดังนั้นเราควรโอเคสำหรับวัตถุประสงค์ส่วนใหญ่:

CREATE TABLE hexdump (hex text); COPY hexdump FROM '/path/file.hex';

ตอนนี้ข้อมูลของเราเป็นสตริงฐานสิบหกที่มีค่ามากเราใช้ PostgresQL decodeเพื่อทำให้เป็นbyteaประเภท:

CREATE TABLE bindump AS SELECT decode(hex, 'hex') FROM hexdump;

โซลูชันนี้ส่งผลให้มีอักขระ \ n ตัวที่ถูกลบออกจากไฟล์
SabreWolfy

2
SabreWolfy: ไม่มันไม่ได้ the tr -d '\n'นั้นทำงานบนเอาต์พุตของ xxd ซึ่งเข้ารหัสเนื้อหาไบนารีของอินพุตเป็นอักขระเลขฐานสิบหก ASCII (0-9 และ af) xxd ยังเกิดขึ้นกับฟีดบรรทัดเอาต์พุตในช่วงเวลาปกติเพื่อให้เอาต์พุตที่มนุษย์อ่านได้ แต่ในกรณีนี้เราต้องการให้ลบออก การป้อนบรรทัดในข้อมูลต้นฉบับจะอยู่ในรูปแบบเลขฐานสิบหกและจะไม่ได้รับผลกระทบใด ๆ
goodside

5

คำตอบกับ xxdเป็นสิ่งที่ดีและสำหรับไฟล์ขนาดเล็กอย่างรวดเร็ว ด้านล่างเป็นสคริปต์ตัวอย่างที่ฉันใช้

xxd  -p /home/user/myimage.png | tr -d '\n' > /tmp/image.hex
echo "
    -- CREATE TABLE hexdump (hex text);
    DELETE FROM hexdump;
    COPY hexdump FROM '/tmp/image.hex';

    -- CREATE TABLE bindump (binarydump bytea);
    DELETE FROM bindump;

    INSERT INTO bindump (binarydump)  
    (SELECT decode(hex, 'hex') FROM hexdump limit 1);

    UPDATE users 
    SET image= 
    (
        SELECT decode(hex, 'hex') 
        FROM hexdump LIMIT 1
    )  
    WHERE id=15489 ;
    " | psql mydatabase

1

ใช้ฟังก์ชันPostgres COPY BINARY นี้เป็นวงกว้างเทียบเท่ากับของออราเคิลตารางภายนอก


ขอบคุณ ลิงค์ที่คุณระบุบ่งชี้ว่าข้อมูลจะต้องอยู่ในรูปแบบตาราง ASCII หรือ PostgreSQL ยิ่งไปกว่านั้นหน้าพูดถึงทำที่รูปแบบตารางไบนารีจะถูกสร้างขึ้นครั้งแรกด้วยคำสั่ง COPY TO วิธีใดวิธีหนึ่งเหล่านี้ทำให้ฉันสามารถแทรกไฟล์ไบนารี (PDF, เอกสาร, สเปรดชีต) ลงในbyteaคอลัมน์ได้หรือไม่
SabreWolfy

เอกสารประกอบ PostgreSQL เกี่ยวกับ COPY BINARY ( postgresql.org/docs/8.4/interactive/sql-copy.html ) ระบุว่าจำเป็นต้องมีส่วนหัวของไฟล์พิเศษเมื่อทำการแทรกข้อมูลไบนารี ฉันต้องสร้างส่วนหัวนี้และผนวกเข้ากับข้อมูลไบนารีหรือไม่ ดูเหมือนจะค่อนข้างซับซ้อนสำหรับการจัดเก็บสตริงข้อมูลไบนารี
SabreWolfy

อืมตอนนี้คุณพูดถึงมันแล้วฉันไม่แน่ใจฉันแค่จำคำสั่งและคิดว่ามันจะทำอย่างนั้น บางที PL / อะไรก็ตามที่เป็นวิธีเดียวที่จะทำได้
ออกุสตุส
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.