899 lines
28 KiB
C++
899 lines
28 KiB
C++
/*------------------------------------------------------------------------------
|
|
* gms_lob.cpp
|
|
*
|
|
* gms_lob内置包的实现
|
|
*
|
|
* Copyright (c) 2002-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 2021, openGauss Contributors
|
|
*
|
|
* IDENTIFICATION
|
|
* contrib/gms_stats/gms_lob.cpp
|
|
*
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "knl/knl_session.h"
|
|
#include "utils/memutils.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "c.h"
|
|
#include "miscadmin.h"
|
|
#include "access/xact.h"
|
|
#include "access/hash.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/syscache.h"
|
|
#include "access/genam.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "executor/spi.h"
|
|
#include "lib/stringinfo.h"
|
|
#include "executor/executor.h"
|
|
#include "catalog/storage_gtt.h"
|
|
#include "utils/numeric.h"
|
|
#include "access/tuptoaster.h"
|
|
|
|
#include "funcapi.h"
|
|
#include "fmgr.h"
|
|
#include "catalog/pg_directory.h"
|
|
#include "mb/pg_wchar.h"
|
|
#include "libpq/pqformat.h"
|
|
#include "storage/ipc.h"
|
|
#include "utils/acl.h"
|
|
#include "utils/bytea.h"
|
|
#include "libpq/be-fsstubs.h"
|
|
#include "libpq/libpq-fs.h"
|
|
#include "commands/extension.h"
|
|
#include "gms_lob.h"
|
|
#include "utils/palloc.h"
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
#define NUMLOB 64
|
|
static uint32 gmslob_index;
|
|
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_createtemporary);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_freetemporary);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_read_blob);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_read_clob);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_write_blob);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_write_clob);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_isopen);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_open);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_append_blob);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_append_clob);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_close);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_cloblength);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_bloblength);
|
|
PG_FUNCTION_INFO_V1(gms_lob_og_null);
|
|
|
|
void init_session_vars(void) {
|
|
RepallocSessionVarsArrayIfNecessary();
|
|
GmsLobContext* psc =
|
|
(GmsLobContext*)MemoryContextAllocZero(u_sess->self_mem_cxt, sizeof(GmsLobContext));
|
|
u_sess->attr.attr_common.extension_session_vars_array[gmslob_index] = psc;
|
|
psc->gmsLobNameHash = NULL;
|
|
}
|
|
|
|
GmsLobContext* get_session_context() {
|
|
if (u_sess->attr.attr_common.extension_session_vars_array[gmslob_index] == NULL) {
|
|
init_session_vars();
|
|
}
|
|
return (GmsLobContext*)u_sess->attr.attr_common.extension_session_vars_array[gmslob_index];
|
|
}
|
|
|
|
static int32 getVarSize(varlena* var)
|
|
{
|
|
if (VARATT_IS_HUGE_TOAST_POINTER(var)) {
|
|
struct varatt_lob_external large_toast_pointer;
|
|
|
|
VARATT_EXTERNAL_GET_HUGE_POINTER(large_toast_pointer, var);
|
|
return large_toast_pointer.va_rawsize;
|
|
} else {
|
|
return VARSIZE_ANY_EXHDR(var);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* charlen_to_bytelen()
|
|
* Compute the number of bytes occupied by n characters starting at *p
|
|
*
|
|
* It is caller's responsibility that there actually are n characters;
|
|
* the string need not be null-terminated.
|
|
*/
|
|
static int charlen_to_bytelen(const char* p, int n)
|
|
{
|
|
if (pg_database_encoding_max_length() == 1) {
|
|
/* Optimization for single-byte encodings */
|
|
return n;
|
|
} else {
|
|
const char* s = NULL;
|
|
|
|
for (s = p; n > 0; n--)
|
|
s += pg_mblen(s);
|
|
|
|
return s - p;
|
|
}
|
|
}
|
|
|
|
/* numeric 向下取整转成int4 */
|
|
int32 numericFloorToInt4(Numeric num)
|
|
{
|
|
Datum datumnum = DirectFunctionCall1(numeric_floor, NumericGetDatum(num));
|
|
return DatumGetInt32(DirectFunctionCall1(numeric_int4, datumnum));
|
|
}
|
|
/*****************************************************************************
|
|
* LOB哈希表相关
|
|
*****************************************************************************/
|
|
struct GmsLobHashKey {
|
|
char* keyvalue;
|
|
int length;
|
|
};
|
|
|
|
typedef struct LobHashEnt {
|
|
GmsLobHashKey key; /* hash表key值 */
|
|
int open_mode; /* 打开方式 */
|
|
} LobHashEnt;
|
|
/*
|
|
* lob数据的hash值生成函数
|
|
*/
|
|
uint32 lob_hash(const void* key, Size kwysize)
|
|
{
|
|
const GmsLobHashKey* hash_key = (const GmsLobHashKey*)key;
|
|
return DatumGetUInt32(hash_any((const unsigned char*)hash_key->keyvalue, hash_key->length));
|
|
}
|
|
/*
|
|
* lob数据的hash比较函数
|
|
*/
|
|
int lob_match(const void* key1, const void* key2, Size kwysize)
|
|
{
|
|
const GmsLobHashKey* k1 = (const GmsLobHashKey*)key1;
|
|
const GmsLobHashKey* k2 = (const GmsLobHashKey*)key2;
|
|
|
|
if (k1->length > k2->length) {
|
|
return 1;
|
|
} else if (k1->length < k2->length) {
|
|
return -1;
|
|
}
|
|
|
|
return strncmp(k1->keyvalue, k2->keyvalue, k1->length);
|
|
}
|
|
/*
|
|
* 创建hash表
|
|
*/
|
|
static HTAB* createlobHash()
|
|
{
|
|
HASHCTL hash_ctl;
|
|
|
|
hash_ctl.keysize = sizeof(GmsLobHashKey);
|
|
hash_ctl.entrysize = sizeof(LobHashEnt);
|
|
hash_ctl.hash = lob_hash;
|
|
hash_ctl.match = lob_match;
|
|
|
|
return hash_create("Lob hash", NUMLOB, &hash_ctl, HASH_ELEM | HASH_FUNCTION | HASH_COMPARE);
|
|
}
|
|
|
|
/*
|
|
* 程序退出时的清理函数
|
|
* 作为回调函数添加到proc_exit()调用的函数列表中
|
|
*/
|
|
static void gms_lob_og_lob_exit(int code, Datum arg)
|
|
{
|
|
HASH_SEQ_STATUS scan;
|
|
LobHashEnt *hentry = NULL;
|
|
|
|
if (get_session_context()->gmsLobNameHash == NULL) {
|
|
return;
|
|
}
|
|
|
|
hash_seq_init(&scan, get_session_context()->gmsLobNameHash);
|
|
while ((hentry = (LobHashEnt *)hash_seq_search(&scan))) {
|
|
if (hentry->key.keyvalue) {
|
|
pfree(hentry->key.keyvalue);
|
|
}
|
|
}
|
|
/* 清理hash表 */
|
|
hash_destroy(get_session_context()->gmsLobNameHash);
|
|
get_session_context()->gmsLobNameHash = NULL;
|
|
}
|
|
|
|
char* generateLobKey(char* argname)
|
|
{
|
|
char* key = NULL;
|
|
int keylen = 0;
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
|
|
TransactionId currentTransactionId = GetCurrentTransactionId();
|
|
// TransactionId采用16进制方式打印,逗号末尾结束符各占一个字节
|
|
keylen = strlen(argname) + sizeof(TransactionId) * 2 + 1 + 1;
|
|
key = (char*)palloc0(keylen);
|
|
errno_t errorno = snprintf_s(key, keylen, keylen - 1, "%16x,%s", currentTransactionId, argname);
|
|
securec_check_ss(errorno, "\0", "\0");
|
|
return key;
|
|
}
|
|
|
|
int searchLob(char* argname, bool* found)
|
|
{
|
|
LobHashEnt* hentry = NULL;
|
|
GmsLobHashKey hash_key;
|
|
if (get_session_context()->gmsLobNameHash) {
|
|
hash_key.keyvalue = generateLobKey(argname);
|
|
hash_key.length = strlen(hash_key.keyvalue) + 1;
|
|
hentry = (LobHashEnt*)hash_search(get_session_context()->gmsLobNameHash, &hash_key, HASH_FIND, NULL);
|
|
if (hentry) {
|
|
*found = true;
|
|
return hentry->open_mode;
|
|
}
|
|
|
|
if (hash_key.keyvalue) {
|
|
pfree(hash_key.keyvalue);
|
|
}
|
|
} else {
|
|
get_session_context()->gmsLobNameHash = createlobHash();
|
|
on_proc_exit(&gms_lob_og_lob_exit, PointerGetDatum(NULL));
|
|
}
|
|
*found = false;
|
|
return -1;
|
|
}
|
|
|
|
static void closeLob(char* argname)
|
|
{
|
|
bool found;
|
|
LobHashEnt* hentry = NULL;
|
|
GmsLobHashKey hash_key;
|
|
|
|
searchLob(argname, &found);
|
|
if (found) {
|
|
hash_key.keyvalue = generateLobKey(argname);
|
|
hash_key.length = strlen(hash_key.keyvalue) + 1;
|
|
hentry = (LobHashEnt*)hash_search(get_session_context()->gmsLobNameHash, &hash_key, HASH_REMOVE, NULL);
|
|
if (!hentry) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("undefined lob.")));
|
|
}
|
|
|
|
if (hash_key.keyvalue) {
|
|
pfree(hash_key.keyvalue);
|
|
}
|
|
|
|
if (hentry->key.keyvalue) {
|
|
pfree(hentry->key.keyvalue);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void openLob(char* argname, int openmode)
|
|
{
|
|
bool found;
|
|
LobHashEnt* hentry = NULL;
|
|
GmsLobHashKey hash_key;
|
|
|
|
hash_key.keyvalue = generateLobKey(argname);
|
|
hash_key.length = strlen(hash_key.keyvalue) + 1;
|
|
hentry = (LobHashEnt*)hash_search(get_session_context()->gmsLobNameHash, &hash_key, HASH_ENTER, &found);
|
|
if (found) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
errmsg("duplicate lob(%s) openned", argname)));
|
|
}
|
|
|
|
hentry->key.keyvalue = (char*)MemoryContextAlloc(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), hash_key.length);
|
|
int rc = memcpy_s(hentry->key.keyvalue, hash_key.length, hash_key.keyvalue, hash_key.length);
|
|
securec_check(rc, "\0", "\0");
|
|
hentry->key.length = hash_key.length;
|
|
hentry->open_mode = openmode;
|
|
|
|
if (hash_key.keyvalue) {
|
|
pfree(hash_key.keyvalue);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.CREATETEMPORARY (
|
|
* lob_loc IN OUT BLOB/CLOB, --blob/clob对象
|
|
* cache IN BOOLEAN, --是否将LOB读取到缓冲区(不生效)
|
|
* dur IN PLS_INTEGER := GMS_LOB.SESSION); --指定何时清除临时LOB(10/ SESSION:会话结束时;12/ CALL:调用结束时)(不生效
|
|
)
|
|
*/
|
|
Datum gms_lob_og_createtemporary(PG_FUNCTION_ARGS)
|
|
{
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(3));
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
|
|
varlena* res = NULL;
|
|
if (PG_ARGISNULL(0)) {
|
|
res = (varlena*)palloc(VARHDRSZ);
|
|
SET_VARSIZE(res, VARHDRSZ);
|
|
PG_RETURN_POINTER(res);
|
|
}
|
|
|
|
PG_RETURN_DATUM(PG_GETARG_DATUM(0));
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.FREETEMPORARY (
|
|
* lob_loc IN OUT BLOB/CLOB); --blob/clob对象
|
|
*/
|
|
Datum gms_lob_og_freetemporary(PG_FUNCTION_ARGS)
|
|
{
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(1));
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
/* lob如果没关闭,将其关闭 */
|
|
closeLob(argname);
|
|
|
|
/* 返回一个空值的varlena */
|
|
varlena* res = (varlena*)palloc0(VARHDRSZ);
|
|
SET_VARSIZE(res, VARHDRSZ);
|
|
|
|
PG_RETURN_DATUM(PointerGetDatum(res));
|
|
}
|
|
|
|
/*
|
|
* lob_loc IN BLOB,
|
|
* amount IN OUT NOCOPY BINARY_INTEGER,
|
|
* offset IN INTEGER,
|
|
* buffer INOUT RAW
|
|
*/
|
|
Datum gms_lob_og_read_blob(PG_FUNCTION_ARGS)
|
|
{
|
|
if (PG_ARGISNULL(0))
|
|
ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),errmsg("invalid LOB object specified")));
|
|
|
|
if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("numeric or value error")));
|
|
}
|
|
|
|
bytea *src_lob = PG_GETARG_BYTEA_P(0);
|
|
int32 amount = PG_GETARG_INT32(1);
|
|
int32 offset = PG_GETARG_INT32(2);
|
|
|
|
if (amount < 1 || amount > AMOUNT_MAX_SIZE) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("amount is invalid or out of range")));
|
|
}
|
|
if (offset < 1 || offset > LOBMAXSIZE) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("offset is invalid or out of range")));
|
|
}
|
|
|
|
int32 srclen = VARSIZE(src_lob) - VARHDRSZ;
|
|
|
|
if (offset > srclen) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("no data found")));
|
|
}
|
|
|
|
int32 srcamount = amount > (srclen - offset + 1) ? (srclen - offset + 1) : amount; //src实际复制的字节数
|
|
bytea* res = NULL;
|
|
int rc = 0;
|
|
res = (bytea*)palloc0(srcamount + VARHDRSZ);
|
|
SET_VARSIZE(res, srcamount + VARHDRSZ);
|
|
rc = memcpy_s(VARDATA(res), srcamount, VARDATA(src_lob) + offset - 1, srcamount);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
TupleDesc tupdesc;
|
|
Datum result;
|
|
HeapTuple tuple;
|
|
Datum values[2];
|
|
bool nulls[2] = { false, false };
|
|
/* 构造返回结果集 */
|
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
|
|
elog(ERROR, "return type must be a row type");
|
|
}
|
|
|
|
/*拼接结果*/
|
|
values[0] = Int32GetDatum(srcamount);
|
|
values[1] = PointerGetDatum(res);
|
|
tuple = heap_form_tuple(tupdesc, values, nulls);
|
|
result = HeapTupleGetDatum(tuple);
|
|
|
|
PG_RETURN_DATUM(result);
|
|
}
|
|
|
|
/*
|
|
* lob_loc IN CLOB,
|
|
* amount IN OUT NOCOPY BINARY_INTEGER,
|
|
* offset IN INTEGER,
|
|
* buffer INOUT VARCHAR2
|
|
*/
|
|
Datum gms_lob_og_read_clob(PG_FUNCTION_ARGS)
|
|
{
|
|
if (PG_ARGISNULL(0))
|
|
ereport(ERROR,(errcode(ERRCODE_INVALID_PARAMETER_VALUE),errmsg("invalid LOB object specified")));
|
|
|
|
if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("numeric or value error")));
|
|
}
|
|
|
|
text* src_lob = PG_GETARG_TEXT_P(0);
|
|
int32 amount = PG_GETARG_INT32(1);
|
|
int32 offset = PG_GETARG_INT32(2);
|
|
|
|
if (amount < 1 || amount > AMOUNT_MAX_SIZE) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("amount is invalid or out of range")));
|
|
}
|
|
|
|
if (offset < 1 || offset > LOBMAXSIZE) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("offset is invalid or out of range")));
|
|
}
|
|
|
|
int32 srclen = DirectFunctionCall1(textlen, PointerGetDatum(src_lob));
|
|
|
|
if (offset > srclen) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("no data found")));
|
|
}
|
|
|
|
int32 srcamount = amount > (srclen - offset + 1) ? (srclen - offset + 1) : amount; //src实际复制的字符数
|
|
int32 srcbytestart = charlen_to_bytelen(VARDATA(src_lob), offset - 1); //src复制起点
|
|
int32 copybyte = charlen_to_bytelen(VARDATA(src_lob) + srcbytestart, srcamount); //src实际复制的字节数
|
|
text* res = NULL;
|
|
int rc = 0;
|
|
|
|
res = (text*)palloc0(copybyte + VARHDRSZ);
|
|
SET_VARSIZE(res, copybyte + VARHDRSZ);
|
|
rc = memcpy_s(VARDATA(res), copybyte, VARDATA(src_lob) + srcbytestart, copybyte);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
TupleDesc tupdesc;
|
|
Datum result;
|
|
HeapTuple tuple;
|
|
Datum values[2];
|
|
bool nulls[2] = { false, false };
|
|
/* 构造返回结果集 */
|
|
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) {
|
|
elog(ERROR, "return type must be a row type");
|
|
}
|
|
|
|
/*拼接结果*/
|
|
values[0] = Int32GetDatum(srcamount);
|
|
values[1] = PointerGetDatum(res);
|
|
|
|
tuple = heap_form_tuple(tupdesc, values, nulls);
|
|
result = HeapTupleGetDatum(tuple);
|
|
|
|
PG_RETURN_DATUM(result);
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.WRITE (
|
|
* lob_loc IN OUT NOCOPY BLOB, --目标blob对象
|
|
* amount IN INTEGER, --指定的传入字节数
|
|
* offset IN INTEGER, --开始读取blob的字节数的偏移量(原点在1)
|
|
* buffer IN RAW); --用于写的输入缓冲区
|
|
*/
|
|
Datum gms_lob_og_write_blob(PG_FUNCTION_ARGS)
|
|
{
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(4));
|
|
/* check input args */
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
if (PG_ARGISNULL(0)) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid LOB object specified")));
|
|
}
|
|
if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Any of the input parameters are NULL")));
|
|
}
|
|
|
|
bytea* destlob = PG_GETARG_BYTEA_P(0);
|
|
int32 amount = numericFloorToInt4(PG_GETARG_NUMERIC(1));
|
|
int32 offset = numericFloorToInt4(PG_GETARG_NUMERIC(2));
|
|
bytea* buffer = PG_GETARG_BYTEA_P(3);
|
|
int32 bufsize = getVarSize(buffer);
|
|
|
|
if (amount < 1 || amount > 32767 || amount > bufsize) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("amount is invalid or out of range")));
|
|
}
|
|
if (offset < 1 || offset > LOBMAXSIZE) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Invalid argument value for dest_offset.")));
|
|
}
|
|
|
|
/* check lob's write and read permissions */
|
|
bool found;
|
|
int32 openmode = searchLob(argname, &found);
|
|
if (found && openmode == 0) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_OPERATION),
|
|
errmsg("cannot update a LOB opened in read-only mode.")));
|
|
}
|
|
|
|
int32 destlen = getVarSize(destlob);
|
|
int destbytestart = (offset - 1) > destlen ? destlen : (offset - 1);
|
|
int32 spacecnt = (offset - 1) > destlen ? (offset - destlen - 1) : 0;
|
|
int32 appendbyte = amount;
|
|
int32 destbyteend = (offset - 1 + appendbyte) > destlen ? destlen : (offset - 1 + appendbyte);
|
|
int64 reslen = destbytestart + spacecnt + appendbyte + (destlen - destbyteend);
|
|
bytea* res = NULL;
|
|
char* resdata = NULL;
|
|
int rc = 0;
|
|
|
|
if (reslen != destlen || buffer == destlob) {
|
|
/* dest is lack of space, or displaced copy when
|
|
* buffer is the same as destlob, to avoid errors
|
|
* in memcpy_s checking, reapply
|
|
*/
|
|
if (reslen > MAX_TOAST_CHUNK_SIZE - VARHDRSZ) {
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Un-support clob/blob type more than 1GB for distributed system")));
|
|
#endif
|
|
}
|
|
res = (bytea*)palloc(reslen + VARHDRSZ);
|
|
SET_VARSIZE(res, reslen + VARHDRSZ);
|
|
} else {
|
|
res = destlob;
|
|
}
|
|
resdata = VARDATA(res);
|
|
if (destbytestart > 0 && res != destlob) {
|
|
rc = memcpy_s(resdata, destbytestart, VARDATA(destlob), destbytestart);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
resdata += destbytestart;
|
|
if (spacecnt > 0) {
|
|
rc = memset_s(resdata, spacecnt, ' ', spacecnt);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
resdata += spacecnt;
|
|
if (appendbyte > 0) {
|
|
rc = memcpy_s(resdata, appendbyte, VARDATA(buffer), appendbyte);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
resdata += appendbyte;
|
|
if (destbyteend < destlen && res != destlob) {
|
|
rc = memcpy_s(resdata, destlen - destbyteend, VARDATA(destlob) + destbyteend, destlen - destbyteend);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
PG_RETURN_BYTEA_P(res);
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.WRITE (
|
|
* lob_loc IN OUT NOCOPY CLOB CHARACTER SET ANY_CS, --目标clob对象
|
|
* amount IN INTEGER, --指定传入的字符数
|
|
* offset IN INTEGER, --开始读取blob的字符数的偏移量(原点在1)
|
|
* buffer IN VARCHAR2 CHARACTER SET lob_loc%CHARSET); --用于写入的缓冲区
|
|
*/
|
|
Datum gms_lob_og_write_clob(PG_FUNCTION_ARGS)
|
|
{
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(4));
|
|
/* check input args */
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
if (PG_ARGISNULL(0)) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid LOB object specified")));
|
|
}
|
|
if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Any of the input parameters are NULL")));
|
|
}
|
|
|
|
text* destlob = PG_GETARG_TEXT_P(0);
|
|
int32 amount = numericFloorToInt4(PG_GETARG_NUMERIC(1));
|
|
int32 offset = numericFloorToInt4(PG_GETARG_NUMERIC(2));
|
|
text* buffer = PG_GETARG_TEXT_P(3);
|
|
int32 bufsize = DirectFunctionCall1(textlen, (Datum)buffer);
|
|
|
|
if (amount < 1 || amount > 32767 || amount > bufsize) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("amount is invalid or out of range")));
|
|
}
|
|
if (offset < 1 || offset > LOBMAXSIZE) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Invalid argument value for dest_offset.")));
|
|
}
|
|
|
|
/* check lob's write and read permissions */
|
|
bool found;
|
|
int32 openmode = searchLob(argname, &found);
|
|
if (found && openmode == 0) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_OPERATION),
|
|
errmsg("cannot update a LOB opened in read-only mode.")));
|
|
}
|
|
|
|
int32 destlen = DirectFunctionCall1(textlen, (Datum)destlob);
|
|
int32 srcamount = amount;
|
|
int32 destbytestart = 0;
|
|
int32 spacecnt = 0;
|
|
int32 appendbyte = charlen_to_bytelen(VARDATA(buffer), srcamount);
|
|
int32 destamount = 0;
|
|
int32 destbyteend = 0;
|
|
int32 destbytecnt = VARSIZE(destlob) - VARHDRSZ;
|
|
int64 reslen = 0;
|
|
text* res = NULL;
|
|
char* resdata = NULL;
|
|
int rc = 0;
|
|
|
|
if (destlen < offset - 1) {
|
|
destamount = destlen;
|
|
spacecnt = offset - 1 - destamount;
|
|
destbytestart = destbytecnt;
|
|
destbyteend = destbytecnt;
|
|
} else {
|
|
destamount = srcamount > (destlen - offset + 1) ? (destlen - offset + 1) : srcamount;
|
|
destbytestart = charlen_to_bytelen(VARDATA(destlob), offset - 1);
|
|
destbyteend = charlen_to_bytelen(VARDATA(destlob) + destbytestart, destamount) + destbytestart;
|
|
}
|
|
reslen = destbytestart + spacecnt + appendbyte + (destbytecnt - destbyteend);
|
|
|
|
if (reslen != destbytecnt || buffer == destlob) {
|
|
/* dest is lack of space, or displaced copy when
|
|
* buffer is the same as destlob, to avoid errors
|
|
* in memcpy_s checking, reapply
|
|
*/
|
|
if (reslen > MAX_TOAST_CHUNK_SIZE - VARHDRSZ) {
|
|
#ifdef ENABLE_MULTIPLE_NODES
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Un-support clob/blob type more than 1GB for distributed system")));
|
|
#endif
|
|
}
|
|
res = (text*)palloc(reslen + VARHDRSZ);
|
|
SET_VARSIZE(res, reslen + VARHDRSZ);
|
|
} else {
|
|
res = destlob;
|
|
}
|
|
resdata = VARDATA(res);
|
|
if (destbytestart > 0 && res != destlob) {
|
|
rc = memcpy_s(resdata, destbytestart, VARDATA(destlob), destbytestart);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
resdata += destbytestart;
|
|
if (spacecnt > 0) {
|
|
rc = memset_s(resdata, spacecnt, ' ', spacecnt);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
resdata += spacecnt;
|
|
if (appendbyte > 0) {
|
|
rc = memcpy_s(resdata, appendbyte, VARDATA(buffer), appendbyte);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
resdata += appendbyte;
|
|
if (destbyteend < destbytecnt && res != destlob) {
|
|
rc = memcpy_s(resdata, destbytecnt - destbyteend, VARDATA(destlob) + destbyteend, destbytecnt - destbyteend);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
PG_RETURN_TEXT_P(res);
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.ISOPEN (
|
|
* lob_loc IN OUT BLOB/CLOB); --blob/clob对象
|
|
*/
|
|
Datum gms_lob_og_isopen(PG_FUNCTION_ARGS)
|
|
{
|
|
bool found;
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(1));
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
searchLob(argname, &found);
|
|
if (found) {
|
|
PG_RETURN_UINT8(1);
|
|
} else {
|
|
PG_RETURN_UINT8(0);
|
|
}
|
|
}
|
|
/*
|
|
* GMS_LOB.OPEN (
|
|
* lob_loc IN OUT BLOB/CLOB,
|
|
* open_mode IN BINARY_INTEGER);
|
|
*/
|
|
Datum gms_lob_og_open(PG_FUNCTION_ARGS)
|
|
{
|
|
if PG_ARGISNULL(0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid LOB object specified")));
|
|
}
|
|
bool found;
|
|
int openmode = PG_GETARG_INT32(1);
|
|
if (openmode != 0 && openmode != 1) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid open_mode")));
|
|
}
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(2));
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
|
|
searchLob(argname, &found);
|
|
if (found) {
|
|
ereport(NOTICE, (errmsg("Lob(%s) already opened in the same transaction", argname)));
|
|
PG_RETURN_DATUM(PG_GETARG_DATUM(0));
|
|
}
|
|
openLob(argname, openmode);
|
|
|
|
PG_RETURN_DATUM(PG_GETARG_DATUM(0));
|
|
}
|
|
|
|
/*
|
|
GMS_LOB.APPEND (
|
|
dest_lob IN OUT NOCOPY BLOB, --目标blob对象
|
|
src_lob IN BLOB); --源blob对象
|
|
*/
|
|
Datum gms_lob_og_append_blob(PG_FUNCTION_ARGS)
|
|
{
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(2));
|
|
/* check input args */
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid LOB object specified")));
|
|
}
|
|
|
|
/* check lob's write and read permissions */
|
|
bool found;
|
|
int32 openmode = searchLob(argname, &found);
|
|
if (found && openmode == 0) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_OPERATION),
|
|
errmsg("cannot update a LOB opened in read-only mode.")));
|
|
}
|
|
|
|
Datum destlob = PG_GETARG_DATUM(0);
|
|
Datum srclob = PG_GETARG_DATUM(1);
|
|
Datum result = DirectFunctionCall2(byteacat, destlob, srclob);
|
|
|
|
PG_RETURN_BYTEA_P(DatumGetPointer(result));
|
|
}
|
|
|
|
/*
|
|
GMS_LOB.APPEND (
|
|
dest_lob IN OUT NOCOPY CLOB CHARACTER SET ANY_CS, --目标clob对象
|
|
src_lob IN CLOB CHARACTER SET dest_lob%CHARSET); --源clob对象
|
|
*/
|
|
Datum gms_lob_og_append_clob(PG_FUNCTION_ARGS)
|
|
{
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(2));
|
|
/* check input args */
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("invalid LOB object specified")));
|
|
}
|
|
|
|
/* check lob's write and read permissions */
|
|
bool found;
|
|
int32 openmode = searchLob(argname, &found);
|
|
if (found && openmode == 0) {
|
|
closeLob(argname);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_OPERATION),
|
|
errmsg("cannot update a LOB opened in read-only mode.")));
|
|
}
|
|
|
|
Datum destlob = PG_GETARG_DATUM(0);
|
|
Datum srclob = PG_GETARG_DATUM(1);
|
|
Datum result = DirectFunctionCall2(textcat, destlob, srclob);
|
|
|
|
PG_RETURN_TEXT_P(DatumGetPointer(result));
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.CLOSE (
|
|
* lob_loc IN OUT BLOB/CLOB); --blob/clob对象
|
|
*/
|
|
Datum gms_lob_og_close(PG_FUNCTION_ARGS)
|
|
{
|
|
char* argname = text_to_cstring(PG_GETARG_TEXT_P(1));
|
|
if (strcmp(argname, ":") == 0) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("output parameter not a bind variable")));
|
|
}
|
|
bool found;
|
|
searchLob(argname, &found);
|
|
if (!found) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("cannot perform operation on an unopened file or LOB")));
|
|
}
|
|
closeLob(argname);
|
|
|
|
PG_RETURN_DATUM(PG_GETARG_DATUM(0));
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.getlength (
|
|
* lob_loc IN OUT CLOB); --clob对象
|
|
*/
|
|
Datum gms_lob_og_cloblength(PG_FUNCTION_ARGS)
|
|
{
|
|
Datum data = PG_GETARG_DATUM(0);
|
|
int datalen = 0;
|
|
|
|
datalen = DirectFunctionCall1(textlen, data);
|
|
PG_RETURN_INT32(datalen);
|
|
}
|
|
/*
|
|
* GMS_LOB.getlength (
|
|
* lob_loc IN OUT BLOB); --blob对象
|
|
*/
|
|
Datum gms_lob_og_bloblength(PG_FUNCTION_ARGS)
|
|
{
|
|
bytea* data = PG_GETARG_BYTEA_P(0);
|
|
int datalen = 0;
|
|
|
|
datalen = VARSIZE(data) - VARHDRSZ;
|
|
PG_RETURN_INT32(datalen);
|
|
}
|
|
|
|
/*
|
|
* GMS_LOB.getlength (); --NULL对象
|
|
*/
|
|
Datum gms_lob_og_null(PG_FUNCTION_ARGS)
|
|
{
|
|
PG_RETURN_VOID();
|
|
} |