Skip to content

Commit bcafd33

Browse files
committed
Adds lo mod
1 parent 99e593c commit bcafd33

File tree

6 files changed

+470
-5
lines changed

6 files changed

+470
-5
lines changed

Cargo.toml

+2-5
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,11 @@ readme = "README.md"
1515
rust-version = "1.77"
1616

1717
[dependencies]
18+
bitflags = "2.0"
1819
libc = "0.2"
1920
log = "0.4"
2021
thiserror = "1.0"
2122

22-
[dependencies.bitflags]
23-
version = "2.0"
24-
optional = true
25-
2623
[dependencies.pq-sys]
2724
package = "libpq-sys"
2825
path = "libpq-sys"
@@ -40,6 +37,6 @@ default = []
4037
v11 = []
4138
v12 = ["v11"]
4239
v13 = ["v12"]
43-
v14 = ["v13", "bitflags"]
40+
v14 = ["v13"]
4441
v15 = ["v14"]
4542
v16 = ["v15"]

examples/testlo.rs

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Test using large objects with libpq
3+
*
4+
* <https://github.com/postgres/postgres/blob/REL_16_0/src/test/examples/testlo.c>
5+
*/
6+
7+
fn main() -> libpq::errors::Result {
8+
let mut args = std::env::args();
9+
10+
if args.len() < 4 {
11+
panic!(
12+
"usage: {} database_name in_filename out_filename",
13+
args.nth(0).unwrap()
14+
);
15+
}
16+
17+
let database = args.nth(1).unwrap();
18+
let in_filename = args.next().unwrap();
19+
let out_filename = args.next().unwrap();
20+
21+
/*
22+
* set up the connection
23+
*/
24+
let conn = libpq::Connection::set_db(None, None, None, None, Some(&database))?;
25+
26+
/* Set always-secure search path, so malicious users can't take control. */
27+
let res = conn.exec("SELECT pg_catalog.set_config('search_path', '', false)");
28+
if res.status() != libpq::Status::TupplesOk {
29+
panic!("SET failed: {:?}", conn.error_message());
30+
}
31+
32+
conn.exec("begin");
33+
println!("importing file \"{in_filename}\" ...");
34+
let lobj_oid = libpq::lo::import(&conn, &in_filename);
35+
36+
println!("\tas large object {lobj_oid}.");
37+
38+
println!("picking out bytes 1000-2000 of the large object");
39+
pickout(&conn, lobj_oid, 1_000, 1_000)?;
40+
41+
println!("overwriting bytes 1000-2000 of the large object with X's");
42+
overwrite(&conn, lobj_oid, 1_000, 1_000)?;
43+
44+
println!("exporting large object to file \"{out_filename}\" ...");
45+
libpq::lo::export(&conn, &out_filename, lobj_oid)?;
46+
47+
conn.exec("end");
48+
49+
Ok(())
50+
}
51+
52+
fn pickout(
53+
conn: &libpq::Connection,
54+
lobj_id: libpq::Oid,
55+
start: i32,
56+
len: usize,
57+
) -> libpq::errors::Result {
58+
let lobj = libpq::lo::open(conn, lobj_id, libpq::lo::Inv::READ)?;
59+
60+
lobj.lseek(start, libpq::lo::Seek::Set)?;
61+
62+
let mut nread = 0;
63+
64+
while len - nread > 0 {
65+
let mut buf = lobj.read(len - nread)?;
66+
let nbytes = buf.len();
67+
buf.insert(nbytes, '\0');
68+
eprint!(">>> {buf}");
69+
nread += nbytes;
70+
if nbytes <= 0 {
71+
break; /* no more data? */
72+
}
73+
}
74+
eprintln!("");
75+
76+
Ok(())
77+
}
78+
79+
fn overwrite(conn: &libpq::Connection, lobj_id: libpq::Oid, start: i32, len: usize) -> libpq::errors::Result {
80+
let lobj = libpq::lo::open(conn, lobj_id, libpq::lo::Inv::WRITE)?;
81+
82+
lobj.lseek(start, libpq::lo::Seek::Set)?;
83+
let mut buf = "X".repeat(len);
84+
buf.insert(len - 1, '\0');
85+
86+
let mut nwritten = 0;
87+
while len - nwritten > 0 {
88+
let nbytes = lobj.write(&buf[nwritten..len - nwritten])?;
89+
nwritten += nbytes;
90+
}
91+
eprintln!("");
92+
93+
Ok(())
94+
}

examples/testlo64.rs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Test using large objects with libpq using 64-bit APIs
3+
*
4+
* <https://github.com/postgres/postgres/blob/REL_16_0/src/test/examples/testlo64.c>
5+
*/
6+
7+
fn main() -> libpq::errors::Result {
8+
let mut args = std::env::args();
9+
10+
if args.len() < 5 {
11+
panic!(
12+
"usage: {} database_name in_filename out_filename out_filename2",
13+
args.nth(0).unwrap()
14+
);
15+
}
16+
17+
let database = args.nth(1).unwrap();
18+
let in_filename = args.next().unwrap();
19+
let out_filename = args.next().unwrap();
20+
let out_filename2 = args.next().unwrap();
21+
22+
/*
23+
* set up the connection
24+
*/
25+
let conn = libpq::Connection::set_db(None, None, None, None, Some(&database))?;
26+
27+
/* Set always-secure search path, so malicious users can't take control. */
28+
let res = conn.exec("SELECT pg_catalog.set_config('search_path', '', false)");
29+
if res.status() != libpq::Status::TupplesOk {
30+
panic!("SET failed: {:?}", conn.error_message());
31+
}
32+
33+
conn.exec("begin");
34+
35+
println!("importing file \"{in_filename}\" ...");
36+
let lobj_oid = libpq::lo::import(&conn, &in_filename);
37+
38+
println!("\tas large object {lobj_oid}.");
39+
40+
println!("picking out bytes 4294967000-4294968000 of the large object");
41+
pickout(&conn, lobj_oid, 4_294_967_000, 1_000)?;
42+
43+
println!("overwriting bytes 4294967000-4294968000 of the large object with X's");
44+
overwrite(&conn, lobj_oid, 4_294_967_000, 1_000)?;
45+
46+
println!("exporting large object to file \"{out_filename}\" ...");
47+
libpq::lo::export(&conn, &out_filename, lobj_oid)?;
48+
49+
println!("truncating to 3294968000 bytes");
50+
my_truncate(&conn, lobj_oid, 3_294_968_000)?;
51+
52+
println!("exporting truncated large object to file \"{out_filename2}\" ...");
53+
libpq::lo::export(&conn, &out_filename2, lobj_oid)?;
54+
55+
conn.exec("end");
56+
57+
Ok(())
58+
}
59+
60+
fn pickout(
61+
conn: &libpq::Connection,
62+
lobj_id: libpq::Oid,
63+
start: i64,
64+
len: usize,
65+
) -> libpq::errors::Result {
66+
let lobj = libpq::lo::open(conn, lobj_id, libpq::lo::Inv::READ)?;
67+
68+
lobj.lseek64(start, libpq::lo::Seek::Set)?;
69+
70+
if lobj.tell64()? != start {
71+
panic!("error in lo_tell64: {:?}", conn.error_message());
72+
}
73+
74+
let mut nread = 0;
75+
76+
while len - nread > 0 {
77+
let mut buf = lobj.read(len - nread)?;
78+
let nbytes = buf.len();
79+
buf.insert(nbytes, '\0');
80+
eprint!(">>> {buf}");
81+
nread += nbytes;
82+
if nbytes <= 0 {
83+
break; /* no more data? */
84+
}
85+
}
86+
eprintln!("");
87+
88+
Ok(())
89+
}
90+
91+
fn overwrite(conn: &libpq::Connection, lobj_id: libpq::Oid, start: i64, len: usize) -> libpq::errors::Result {
92+
let lobj = libpq::lo::open(conn, lobj_id, libpq::lo::Inv::WRITE)?;
93+
94+
lobj.lseek64(start, libpq::lo::Seek::Set)?;
95+
let mut buf = "X".repeat(len);
96+
buf.insert(len - 1, '\0');
97+
98+
let mut nwritten = 0;
99+
while len - nwritten > 0 {
100+
let nbytes = lobj.write(&buf[nwritten..len - nwritten])?;
101+
nwritten += nbytes;
102+
}
103+
eprintln!("");
104+
105+
Ok(())
106+
}
107+
108+
fn my_truncate(conn: &libpq::Connection, lobj_id: libpq::Oid, len: i64) -> libpq::errors::Result {
109+
let lobj = libpq::lo::open(conn, lobj_id, libpq::lo::Inv::WRITE)?;
110+
111+
lobj.truncate64(len)
112+
}

src/errors.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub enum Error {
88
NulError(#[from] std::ffi::NulError),
99
#[error("{0}")]
1010
Backend(String),
11+
#[error("Large object error")]
12+
LargeObject,
1113
#[error("Unknow error")]
1214
Unknow,
1315
#[error("{0}")]

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod connection;
88
pub mod encrypt;
99
pub mod errors;
1010
pub mod escape;
11+
pub mod lo;
1112
pub mod ping;
1213
#[cfg(feature = "v14")]
1314
pub mod pipeline;
@@ -31,6 +32,7 @@ mod verbosity;
3132
pub use connection::Connection;
3233
pub use encoding::*;
3334
pub use format::*;
35+
pub use lo::LargeObject;
3436
pub use oid::*;
3537
pub use result::Result;
3638
pub use state::State;

0 commit comments

Comments
 (0)