问题
I'm using PostgreSQL 9.2.
This blog entry by Grace Batumbya provides a cast from bytea to oid.
create or replace function blob_write(lbytea bytea)
returns oid
volatile
language plpgsql as
$f$
declare
loid oid;
lfd integer;
lsize integer;
begin
if(lbytea is null) then
return null;
end if;
loid := lo_create(0);
lfd := lo_open(loid,131072);
lsize := lowrite(lfd,lbytea);
perform lo_close(lfd);
return loid;
end;
$f$;
CREATE CAST (bytea AS oid) WITH FUNCTION blob_write(bytea) AS ASSIGNMENT;
CREATE TABLE bytea_to_lo (
largeObj lo
);
I didn't understand why should we create bytea_to_lo table? How is it going to be used by PostgreSQL?
回答1:
The cast is not a true cast. It's just (ab)using the convenient syntax. A large object (LO) is created in the background which is stored separately and the OID referencing it is returned.
Per documentation:
All large objects are stored in a single system table named
pg_largeobject. Each large object also has an entry in the system tablepg_largeobject_metadata. Large objects can be created, modified, and deleted using a read/write API that is similar to standard operations on files.
The returned OID is basically a FK to the PK of the system table pg_largeobject.
CREATE TABLE is completely independent from the function and pseudo-cast.
CREATE TABLE bytea_to_lo (
largeObj lo
);
It's just a typical use case for the assignment cast created above, which becomes apparent from the following line that you forgot to quote:
INSERT INTO bytea_to_lo VALUES (DECODE('00AB','hex'));
What happens here?
The data type lo is a domain over the base type oid, created by the additional module lo (incorrectly referenced as "lo_manage package" in the blog enty of Grace Batumbya). Per documentation:
The module also provides a data type
lo, which is really just a domain of theoidtype. This is useful for differentiating database columns that hold large object references from those that are OIDs of other things.
The function decode() returns bytea. The INSERT statement assigns the bytea value to the column largeObj, which triggers an assignment cast to its type lo, and that's where the above cast comes in.
Warning / Corrective / Update
The blog entry is sloppy and outdated by now.
Does not bother to mention that (per documentation):
To be able to create a cast, you must own the source or the target data type and have
USAGEprivilege on the other type.Effectively, you must be superuser.
Typo in
CREATE TABLE: column name and type reversed.The function definition is verbose and inefficient. This would be better (for Postgres 9.3 or older):
CREATE OR REPLACE FUNCTION blob_write(bytea) RETURNS oid AS $func$ DECLARE loid oid := lo_create(0); lfd int := lo_open(loid,131072); -- = 2^17 = x2000 -- symbolic constant defined in the header file libpq/libpq-fs.h -- #define INV_WRITE 0x00020000 BEGIN PERFORM lowrite(lfd, $1); PERFORM lo_close(lfd); RETURN loid; END $func$ LANGUAGE plpgsql VOLATILE STRICT;SQL Fiddle.
There is a built-in function for this in Postgres 9.4. Use that instead:
lo_from_bytea(loid oid, string bytea)
From the release notes:
- Add SQL functions to allow large object reads/writes at arbitrary offsets (Pavel Stehule)
For CREATE CAST (per documentation):
The first argument type must be identical to or binary-coercible from the cast's source type.
I suggest an overloaded variant with only a bytea parameter:
CREATE OR REPLACE FUNCTION lo_from_bytea(bytea)
RETURNS oid LANGUAGE sql AS
'SELECT lo_from_bytea(0, $1)';
CREATE CAST (bytea AS oid) WITH FUNCTION lo_from_bytea(bytea) AS ASSIGNMENT;
Since the pseudo-cast has quite a big side effect, I am not convinced to make that an ASSIGNMENT cast. I'd probably start with explicit-only:
- Generate series of dates - using date type as input
来源:https://stackoverflow.com/questions/29433041/understanding-cast-from-bytea-to-oid