forked from huawei/openGauss-server
1614 lines
51 KiB
C++
1614 lines
51 KiB
C++
/*
|
|
* foreign.cpp
|
|
* support for foreign-data wrappers, servers and user mappings.
|
|
*
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/cbb/extension/foreign/foreign.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include "access/reloptions.h"
|
|
#include "catalog/pg_foreign_data_wrapper.h"
|
|
#include "catalog/pg_foreign_server.h"
|
|
#include "catalog/pg_foreign_table.h"
|
|
#include "catalog/pg_user_mapping.h"
|
|
#include "foreign/dummyserver.h"
|
|
#include "foreign/regioninfo.h"
|
|
#include "foreign/fdwapi.h"
|
|
#include "foreign/foreign.h"
|
|
#include "lib/stringinfo.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/parsenodes.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/int8.h"
|
|
#include "utils/memutils.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/rel_gs.h"
|
|
#include "utils/syscache.h"
|
|
#include "cipher.h"
|
|
|
|
extern Datum pg_options_to_table(PG_FUNCTION_ARGS);
|
|
extern Datum postgresql_fdw_validator(PG_FUNCTION_ARGS);
|
|
|
|
extern void CheckGetServerIpAndPort(const char* Address, List** AddrList, bool IsCheck, int real_addr_max);
|
|
extern void decryptKeyString(
|
|
const char* keyStr, char destplainStr[], uint32 destplainLength, const char* obskey = NULL);
|
|
|
|
char* pg_strrstr(char* string, const char* subStr);
|
|
|
|
char* rebuildLocationOption(char* region, char* location);
|
|
|
|
/*
|
|
* GetForeignDataWrapper - look up the foreign-data wrapper by OID.
|
|
*/
|
|
ForeignDataWrapper* GetForeignDataWrapper(Oid fdwid)
|
|
{
|
|
Form_pg_foreign_data_wrapper fdwform;
|
|
ForeignDataWrapper* fdw = NULL;
|
|
Datum datum;
|
|
HeapTuple tp;
|
|
bool isnull = false;
|
|
|
|
tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
|
|
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for foreign-data wrapper %u", fdwid)));
|
|
}
|
|
|
|
fdwform = (Form_pg_foreign_data_wrapper)GETSTRUCT(tp);
|
|
|
|
fdw = (ForeignDataWrapper*)palloc(sizeof(ForeignDataWrapper));
|
|
fdw->fdwid = fdwid;
|
|
fdw->owner = fdwform->fdwowner;
|
|
fdw->fdwname = pstrdup(NameStr(fdwform->fdwname));
|
|
fdw->fdwhandler = fdwform->fdwhandler;
|
|
fdw->fdwvalidator = fdwform->fdwvalidator;
|
|
|
|
/* Extract the fdwoptions */
|
|
datum = SysCacheGetAttr(FOREIGNDATAWRAPPEROID, tp, Anum_pg_foreign_data_wrapper_fdwoptions, &isnull);
|
|
if (isnull) {
|
|
fdw->options = NIL;
|
|
} else {
|
|
fdw->options = untransformRelOptions(datum);
|
|
}
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
return fdw;
|
|
}
|
|
|
|
/*
|
|
* GetForeignDataWrapperByName - look up the foreign-data wrapper
|
|
* definition by name.
|
|
*/
|
|
ForeignDataWrapper* GetForeignDataWrapperByName(const char* fdwname, bool missing_ok)
|
|
{
|
|
Oid fdwId = get_foreign_data_wrapper_oid(fdwname, missing_ok);
|
|
|
|
if (!OidIsValid(fdwId)) {
|
|
return NULL;
|
|
}
|
|
|
|
return GetForeignDataWrapper(fdwId);
|
|
}
|
|
|
|
/*
|
|
* GetForeignServer - look up the foreign server name by serverid.
|
|
*/
|
|
char* GetForeignServerName(Oid serverid)
|
|
{
|
|
Form_pg_foreign_server serverform;
|
|
char* server_name = NULL;
|
|
HeapTuple tp;
|
|
|
|
tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
|
|
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for foreign server %u", serverid)));
|
|
}
|
|
|
|
serverform = (Form_pg_foreign_server)GETSTRUCT(tp);
|
|
server_name = pstrdup(NameStr(serverform->srvname));
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
return server_name;
|
|
}
|
|
|
|
/*
|
|
* GetForeignServer - look up the foreign server definition.
|
|
*/
|
|
ForeignServer* GetForeignServer(Oid serverid)
|
|
{
|
|
Form_pg_foreign_server serverform;
|
|
ForeignServer* server = NULL;
|
|
HeapTuple tp;
|
|
Datum datum;
|
|
bool isnull = false;
|
|
|
|
tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
|
|
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(
|
|
ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for foreign server %u", serverid)));
|
|
}
|
|
|
|
serverform = (Form_pg_foreign_server)GETSTRUCT(tp);
|
|
|
|
server = (ForeignServer*)palloc(sizeof(ForeignServer));
|
|
server->serverid = serverid;
|
|
server->servername = pstrdup(NameStr(serverform->srvname));
|
|
server->owner = serverform->srvowner;
|
|
server->fdwid = serverform->srvfdw;
|
|
|
|
/* Extract server type */
|
|
datum = SysCacheGetAttr(FOREIGNSERVEROID, tp, Anum_pg_foreign_server_srvtype, &isnull);
|
|
server->servertype = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
|
|
|
|
/* Extract server version */
|
|
datum = SysCacheGetAttr(FOREIGNSERVEROID, tp, Anum_pg_foreign_server_srvversion, &isnull);
|
|
server->serverversion = isnull ? NULL : pstrdup(TextDatumGetCString(datum));
|
|
|
|
/* Extract the srvoptions */
|
|
datum = SysCacheGetAttr(FOREIGNSERVEROID, tp, Anum_pg_foreign_server_srvoptions, &isnull);
|
|
if (isnull) {
|
|
server->options = NIL;
|
|
} else {
|
|
server->options = untransformRelOptions(datum);
|
|
}
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
return server;
|
|
}
|
|
|
|
/*
|
|
* GetForeignServerByName - look up the foreign server definition by name.
|
|
*/
|
|
ForeignServer* GetForeignServerByName(const char* srvname, bool missing_ok)
|
|
{
|
|
Oid serverid = get_foreign_server_oid(srvname, missing_ok);
|
|
|
|
if (!OidIsValid(serverid)) {
|
|
return NULL;
|
|
}
|
|
|
|
return GetForeignServer(serverid);
|
|
}
|
|
|
|
/* Jude whether type of the foreign table equal to the specified type or not.
|
|
* ServerName: the server name of foreign table.
|
|
* SepcifiedType: the given type of foreign table.
|
|
*/
|
|
bool IsSpecifiedFDW(const char* ServerName, const char* SepcifiedType)
|
|
{
|
|
ForeignServer* Server = GetForeignServerByName(ServerName, false);
|
|
|
|
/* GetForeignServerByname may return NULL. */
|
|
if (NULL == Server) {
|
|
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("foreign table server is invalid.")));
|
|
}
|
|
|
|
ForeignDataWrapper* Fdw = GetForeignDataWrapper(Server->fdwid);
|
|
|
|
if (0 == pg_strcasecmp(Fdw->fdwname, SepcifiedType)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @Description: Jude whether type of the foreign table equal to the specified type or not.
|
|
* @in relId: The foreign table Oid.
|
|
* @in SepcifiedType: The given type of foreign table.
|
|
* @return If the relation type equal to the given type of foreign table, return true,
|
|
* otherwise return false.
|
|
*/
|
|
bool IsSpecifiedFDWFromRelid(Oid relId, const char* SepcifiedType)
|
|
{
|
|
ForeignTable* ftbl = NULL;
|
|
ForeignServer* fsvr = NULL;
|
|
bool IsSpecifiedTable = false;
|
|
|
|
if (u_sess->opt_cxt.ft_context == NULL) {
|
|
u_sess->opt_cxt.ft_context = AllocSetContextCreate(u_sess->top_mem_cxt,
|
|
"ForeignTableTemp1",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
} else {
|
|
MemoryContextReset(u_sess->opt_cxt.ft_context);
|
|
}
|
|
MemoryContext old_context = MemoryContextSwitchTo(u_sess->opt_cxt.ft_context);
|
|
|
|
ftbl = GetForeignTable(relId);
|
|
Assert(NULL != ftbl);
|
|
|
|
fsvr = GetForeignServer(ftbl->serverid);
|
|
Assert(NULL != fsvr);
|
|
|
|
if (IsSpecifiedFDW(fsvr->servername, SepcifiedType)) {
|
|
IsSpecifiedTable = true;
|
|
}
|
|
|
|
MemoryContextSwitchTo(old_context);
|
|
MemoryContextReset(u_sess->opt_cxt.ft_context);
|
|
|
|
return IsSpecifiedTable;
|
|
}
|
|
|
|
/**
|
|
* @Description: Jude whether type of the foreign table support SELECT/INSERT/UPDATE/DELETE/COPY
|
|
* @in relId: The foreign table Oid.
|
|
* @return Rreturn true if the foreign table support those DML.
|
|
*/
|
|
bool CheckSupportedFDWType(Oid relId)
|
|
{
|
|
static const char* supportFDWType[] = {MOT_FDW, MYSQL_FDW, ORACLE_FDW, POSTGRES_FDW};
|
|
int size = sizeof(supportFDWType) / sizeof(supportFDWType[0]);
|
|
bool support = false;
|
|
|
|
if (u_sess->opt_cxt.ft_context == NULL) {
|
|
u_sess->opt_cxt.ft_context = AllocSetContextCreate(u_sess->top_mem_cxt,
|
|
"ForeignTableTemp1",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
} else {
|
|
MemoryContextReset(u_sess->opt_cxt.ft_context);
|
|
}
|
|
MemoryContext oldContext = MemoryContextSwitchTo(u_sess->opt_cxt.ft_context);
|
|
|
|
ForeignTable* ftbl = GetForeignTable(relId);
|
|
ForeignServer* fsvr = GetForeignServer(ftbl->serverid);
|
|
ForeignDataWrapper* fdw = GetForeignDataWrapper(fsvr->fdwid);
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
if (pg_strcasecmp(fdw->fdwname, supportFDWType[i]) == 0) {
|
|
support = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
MemoryContextReset(u_sess->opt_cxt.ft_context);
|
|
return support;
|
|
}
|
|
|
|
bool isSpecifiedSrvTypeFromRelId(Oid relId, const char* SepcifiedType)
|
|
{
|
|
ForeignTable* ftbl = NULL;
|
|
ForeignServer* fsrv = NULL;
|
|
bool ret = false;
|
|
|
|
if (u_sess->opt_cxt.ft_context == NULL) {
|
|
u_sess->opt_cxt.ft_context = AllocSetContextCreate(u_sess->top_mem_cxt,
|
|
"ForeignTableTemp2",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
} else {
|
|
MemoryContextReset(u_sess->opt_cxt.ft_context);
|
|
}
|
|
MemoryContext old_context = MemoryContextSwitchTo(u_sess->opt_cxt.ft_context);
|
|
|
|
ftbl = GetForeignTable(relId);
|
|
Assert(NULL != ftbl);
|
|
|
|
fsrv = GetForeignServer(ftbl->serverid);
|
|
Assert(NULL != fsrv);
|
|
|
|
ret = isSpecifiedSrvTypeFromSrvName(fsrv->servername, SepcifiedType);
|
|
|
|
MemoryContextSwitchTo(old_context);
|
|
MemoryContextReset(u_sess->opt_cxt.ft_context);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool isSpecifiedSrvTypeFromSrvName(const char* srvName, const char* SepcifiedType)
|
|
{
|
|
ForeignServer* Server = GetForeignServerByName(srvName, false);
|
|
/* GetForeignServerByname may return NULL. */
|
|
if (NULL == Server) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_SYNTAX_ERROR),
|
|
errmsg("cannot find foreign server with given name %s .", srvName)));
|
|
}
|
|
|
|
bool isSpecifiedSrvType = false;
|
|
ListCell* optionCell = NULL;
|
|
|
|
foreach (optionCell, Server->options) {
|
|
DefElem* optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (0 == pg_strcasecmp(optionDefName, "type")) {
|
|
char* optionValue = defGetString(optionDef);
|
|
if (0 == pg_strcasecmp(optionValue, SepcifiedType)) {
|
|
isSpecifiedSrvType = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isSpecifiedSrvType;
|
|
}
|
|
|
|
static void DecryptOptions(List *options)
|
|
{
|
|
if (options == NULL) {
|
|
return;
|
|
}
|
|
|
|
ListCell *cell = NULL;
|
|
foreach(cell, options) {
|
|
DefElem* def = (DefElem*)lfirst(cell);
|
|
if (def->defname == NULL || def->arg == NULL || !IsA(def->arg, String)) {
|
|
continue;
|
|
}
|
|
|
|
char *str = strVal(def->arg);
|
|
if (str == NULL || strlen(str) == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (int i = 0; i < sensitiveArrayLength; i++) {
|
|
if (pg_strcasecmp(def->defname, sensitiveOptionsArray[i]) == 0) {
|
|
char plainText[EC_CIPHER_TEXT_LENGTH] = {0};
|
|
|
|
/*
|
|
* If decryptECString return false, it means the stored values is not encrypted.
|
|
* This happened when user mapping was created in old gaussdb version.
|
|
*/
|
|
if (decryptECString(str, plainText, EC_CIPHER_TEXT_LENGTH, false)) {
|
|
pfree_ext(str);
|
|
pfree_ext(def->arg);
|
|
def->arg = (Node*)makeString(pstrdup(plainText));
|
|
|
|
/* Clear buffer */
|
|
errno_t errCode = memset_s(plainText, EC_CIPHER_TEXT_LENGTH, 0, EC_CIPHER_TEXT_LENGTH);
|
|
securec_check(errCode, "\0", "\0");
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* GetUserMapping - look up the user mapping.
|
|
*
|
|
* If no mapping is found for the supplied user, we also look for
|
|
* PUBLIC mappings (userid == InvalidOid).
|
|
*/
|
|
UserMapping* GetUserMapping(Oid userid, Oid serverid)
|
|
{
|
|
Datum datum;
|
|
HeapTuple tp;
|
|
bool isnull = false;
|
|
UserMapping* um = NULL;
|
|
|
|
tp = SearchSysCache2(USERMAPPINGUSERSERVER, ObjectIdGetDatum(userid), ObjectIdGetDatum(serverid));
|
|
|
|
if (!HeapTupleIsValid(tp)) {
|
|
/* Not found for the specific user -- try PUBLIC */
|
|
tp = SearchSysCache2(USERMAPPINGUSERSERVER, ObjectIdGetDatum(InvalidOid), ObjectIdGetDatum(serverid));
|
|
}
|
|
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("user mapping not found for \"%s\"", MappingUserName(userid))));
|
|
}
|
|
|
|
um = (UserMapping*)palloc(sizeof(UserMapping));
|
|
um->userid = userid;
|
|
um->serverid = serverid;
|
|
|
|
/* Extract the umoptions */
|
|
datum = SysCacheGetAttr(USERMAPPINGUSERSERVER, tp, Anum_pg_user_mapping_umoptions, &isnull);
|
|
if (isnull) {
|
|
um->options = NIL;
|
|
} else {
|
|
um->options = untransformRelOptions(datum);
|
|
}
|
|
|
|
DecryptOptions(um->options);
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
return um;
|
|
}
|
|
|
|
/*
|
|
* GetForeignTable - look up the foreign table definition by relation oid.
|
|
*/
|
|
ForeignTable* GetForeignTable(Oid relid)
|
|
{
|
|
Form_pg_foreign_table tableform;
|
|
ForeignTable* ft = NULL;
|
|
HeapTuple tp;
|
|
Datum datum;
|
|
bool isnull = false;
|
|
|
|
tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for foreign table %u", relid)));
|
|
}
|
|
tableform = (Form_pg_foreign_table)GETSTRUCT(tp);
|
|
|
|
ft = (ForeignTable*)palloc(sizeof(ForeignTable));
|
|
ft->relid = relid;
|
|
ft->serverid = tableform->ftserver;
|
|
ft->write_only = tableform->ftwriteonly;
|
|
|
|
/* Extract the ftoptions */
|
|
datum = SysCacheGetAttr(FOREIGNTABLEREL, tp, Anum_pg_foreign_table_ftoptions, &isnull);
|
|
if (isnull) {
|
|
ft->options = NIL;
|
|
} else {
|
|
ft->options = untransformRelOptions(datum);
|
|
}
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
return ft;
|
|
}
|
|
|
|
/**
|
|
* @Description: Juge the foreign table is write only table?
|
|
* @in relid, the foreign table oid.
|
|
* @return return true if the foreign talbe is write-only mode, otherwise return false.
|
|
*/
|
|
bool isWriteOnlyFt(Oid relid)
|
|
{
|
|
Form_pg_foreign_table tableform;
|
|
HeapTuple tp;
|
|
|
|
tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for foreign table %u", relid)));
|
|
}
|
|
tableform = (Form_pg_foreign_table)GETSTRUCT(tp);
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
return tableform->ftwriteonly;
|
|
}
|
|
|
|
/*
|
|
* GetForeignColumnOptions - Get attfdwoptions of given relation/attnum
|
|
* as list of DefElem.
|
|
*/
|
|
List* GetForeignColumnOptions(Oid relid, AttrNumber attnum)
|
|
{
|
|
List* options = NIL;
|
|
HeapTuple tp;
|
|
Datum datum;
|
|
bool isnull = false;
|
|
|
|
tp = SearchSysCache2(ATTNUM, ObjectIdGetDatum(relid), Int16GetDatum(attnum));
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("cache lookup failed for attribute %d of relation %u", attnum, relid)));
|
|
}
|
|
datum = SysCacheGetAttr(ATTNUM, tp, Anum_pg_attribute_attfdwoptions, &isnull);
|
|
if (isnull) {
|
|
options = NIL;
|
|
} else {
|
|
options = untransformRelOptions(datum);
|
|
}
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
return options;
|
|
}
|
|
|
|
/*
|
|
* GetFdwRoutine - call the specified foreign-data wrapper handler routine
|
|
* to get its FdwRoutine struct.
|
|
*/
|
|
FdwRoutine* GetFdwRoutine(Oid fdwhandler)
|
|
{
|
|
Datum datum;
|
|
FdwRoutine* routine = NULL;
|
|
|
|
datum = OidFunctionCall0(fdwhandler);
|
|
routine = (FdwRoutine*)DatumGetPointer(datum);
|
|
|
|
if (routine == NULL || !IsA(routine, FdwRoutine)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FDW_INVALID_HANDLE),
|
|
errmsg("foreign-data wrapper handler function %u did not return an FdwRoutine struct", fdwhandler)));
|
|
}
|
|
|
|
return routine;
|
|
}
|
|
|
|
/*
|
|
* GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
|
|
* for the given foreign table, and retrieve its FdwRoutine struct.
|
|
*/
|
|
FdwRoutine* GetFdwRoutineByRelId(Oid relid)
|
|
{
|
|
HeapTuple tp;
|
|
Form_pg_foreign_data_wrapper fdwform;
|
|
Form_pg_foreign_server serverform;
|
|
Form_pg_foreign_table tableform;
|
|
Oid serverid;
|
|
Oid fdwid;
|
|
Oid fdwhandler;
|
|
|
|
/* Get server OID for the foreign table. */
|
|
tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for foreign table %u", relid)));
|
|
}
|
|
tableform = (Form_pg_foreign_table)GETSTRUCT(tp);
|
|
serverid = tableform->ftserver;
|
|
ReleaseSysCache(tp);
|
|
|
|
/* Get foreign-data wrapper OID for the server. */
|
|
tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(
|
|
ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for foreign table %u", serverid)));
|
|
}
|
|
serverform = (Form_pg_foreign_server)GETSTRUCT(tp);
|
|
fdwid = serverform->srvfdw;
|
|
ReleaseSysCache(tp);
|
|
|
|
/* Get handler function OID for the FDW. */
|
|
tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
|
|
if (!HeapTupleIsValid(tp)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("cache lookup failed for foreign-data wrapper %u", fdwid)));
|
|
}
|
|
fdwform = (Form_pg_foreign_data_wrapper)GETSTRUCT(tp);
|
|
fdwhandler = fdwform->fdwhandler;
|
|
|
|
/* Complain if FDW has been set to NO HANDLER. */
|
|
if (!OidIsValid(fdwhandler)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
errmsg("foreign-data wrapper \"%s\" has no handler", NameStr(fdwform->fdwname))));
|
|
}
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
/* And finally, call the handler function. */
|
|
return GetFdwRoutine(fdwhandler);
|
|
}
|
|
|
|
/*
|
|
* GetFdwRoutineByServerId - look up the handler of the foreign-data wrapper
|
|
* for the given foreign table, and retrieve its FdwRoutine struct.
|
|
*/
|
|
FdwRoutine* GetFdwRoutineByServerId(Oid serverid)
|
|
{
|
|
HeapTuple tp;
|
|
Form_pg_foreign_data_wrapper fdwform;
|
|
Form_pg_foreign_server serverform;
|
|
Oid fdwid;
|
|
Oid fdwhandler;
|
|
|
|
/* Get foreign-data wrapper OID for the server. */
|
|
tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
|
|
if (!HeapTupleIsValid(tp))
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("cache lookup failed for foreign server %u", serverid)));
|
|
serverform = (Form_pg_foreign_server) GETSTRUCT(tp);
|
|
fdwid = serverform->srvfdw;
|
|
ReleaseSysCache(tp);
|
|
|
|
/* Get handler function OID for the FDW. */
|
|
tp = SearchSysCache1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid));
|
|
if (!HeapTupleIsValid(tp))
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("cache lookup failed for foreign-data wrapper %u", fdwid)));
|
|
fdwform = (Form_pg_foreign_data_wrapper) GETSTRUCT(tp);
|
|
fdwhandler = fdwform->fdwhandler;
|
|
|
|
/* Complain if FDW has been set to NO HANDLER. */
|
|
if (!OidIsValid(fdwhandler))
|
|
ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
|
errmsg("foreign-data wrapper \"%s\" has no handler", NameStr(fdwform->fdwname))));
|
|
|
|
ReleaseSysCache(tp);
|
|
|
|
/* And finally, call the handler function. */
|
|
return GetFdwRoutine(fdwhandler);
|
|
}
|
|
|
|
/*
|
|
* GetFdwRoutineForRelation - look up the handler of the foreign-data wrapper
|
|
* for the given foreign table, and retrieve its FdwRoutine struct.
|
|
*
|
|
* This function is preferred over GetFdwRoutineByRelId because it caches
|
|
* the data in the relcache entry, saving a number of catalog lookups.
|
|
*
|
|
* If makecopy is true then the returned data is freshly palloc'd in the
|
|
* caller's memory context. Otherwise, it's a pointer to the relcache data,
|
|
* which will be lost in any relcache reset --- so don't rely on it long.
|
|
*/
|
|
FdwRoutine* GetFdwRoutineForRelation(Relation relation, bool makecopy)
|
|
{
|
|
FdwRoutine* fdwroutine = NULL;
|
|
FdwRoutine* cfdwroutine = NULL;
|
|
errno_t rc = EOK;
|
|
|
|
if (relation->rd_fdwroutine == NULL) {
|
|
/* Get the info by consulting the catalogs and the FDW code */
|
|
fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(relation));
|
|
|
|
/* Save the data for later reuse in u_sess->cache_mem_cxt */
|
|
cfdwroutine = (FdwRoutine*)MemoryContextAlloc(u_sess->cache_mem_cxt, sizeof(FdwRoutine));
|
|
rc = memcpy_s(cfdwroutine, sizeof(FdwRoutine), fdwroutine, sizeof(FdwRoutine));
|
|
securec_check(rc, "", "");
|
|
|
|
relation->rd_fdwroutine = cfdwroutine;
|
|
|
|
/* Give back the locally palloc'd copy regardless of makecopy */
|
|
return fdwroutine;
|
|
}
|
|
|
|
/* We have valid cached data --- does the caller want a copy? */
|
|
if (makecopy) {
|
|
fdwroutine = (FdwRoutine*)palloc(sizeof(FdwRoutine));
|
|
rc = memcpy_s(fdwroutine, sizeof(FdwRoutine), relation->rd_fdwroutine, sizeof(FdwRoutine));
|
|
securec_check(rc, "", "");
|
|
return fdwroutine;
|
|
}
|
|
|
|
/* Only a short-lived reference is needed, so just hand back cached copy */
|
|
return relation->rd_fdwroutine;
|
|
}
|
|
|
|
/*
|
|
* deflist_to_tuplestore - Helper function to convert DefElem list to
|
|
* tuplestore usable in SRF.
|
|
*/
|
|
static void deflist_to_tuplestore(ReturnSetInfo* rsinfo, List* options)
|
|
{
|
|
ListCell* cell = NULL;
|
|
TupleDesc tupdesc;
|
|
Tuplestorestate* tupstore = NULL;
|
|
Datum values[2];
|
|
bool nulls[2];
|
|
MemoryContext per_query_ctx;
|
|
MemoryContext oldcontext;
|
|
|
|
/* check to see if caller supports us returning a tuplestore */
|
|
if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("set-valued function called in context that cannot accept a set")));
|
|
}
|
|
if (!(rsinfo->allowedModes & SFRM_Materialize) || rsinfo->expectedDesc == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("materialize mode required, but it is not allowed in this context")));
|
|
}
|
|
|
|
per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
|
|
oldcontext = MemoryContextSwitchTo(per_query_ctx);
|
|
|
|
/*
|
|
* Now prepare the result set.
|
|
*/
|
|
tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
|
|
tupstore = tuplestore_begin_heap(true, false, u_sess->attr.attr_memory.work_mem);
|
|
rsinfo->returnMode = SFRM_Materialize;
|
|
rsinfo->setResult = tupstore;
|
|
rsinfo->setDesc = tupdesc;
|
|
|
|
foreach (cell, options) {
|
|
DefElem* def = (DefElem*)lfirst(cell);
|
|
|
|
values[0] = CStringGetTextDatum(def->defname);
|
|
nulls[0] = false;
|
|
if (def->arg) {
|
|
values[1] = CStringGetTextDatum(((Value*)(def->arg))->val.str);
|
|
nulls[1] = false;
|
|
} else {
|
|
values[1] = (Datum)0;
|
|
nulls[1] = true;
|
|
}
|
|
tuplestore_putvalues(tupstore, tupdesc, values, nulls);
|
|
}
|
|
|
|
/* clean up and return the tuplestore */
|
|
tuplestore_donestoring(tupstore);
|
|
|
|
MemoryContextSwitchTo(oldcontext);
|
|
}
|
|
|
|
/*
|
|
* Convert options array to name/value table. Useful for information
|
|
* schema and pg_dump.
|
|
*/
|
|
Datum pg_options_to_table(PG_FUNCTION_ARGS)
|
|
{
|
|
Datum array = PG_GETARG_DATUM(0);
|
|
|
|
deflist_to_tuplestore((ReturnSetInfo*)fcinfo->resultinfo, untransformRelOptions(array));
|
|
|
|
return (Datum)0;
|
|
}
|
|
|
|
/*
|
|
* Describes the valid options for postgresql FDW, server, and user mapping.
|
|
*/
|
|
struct ConnectionOption {
|
|
const char* optname;
|
|
Oid optcontext; /* Oid of catalog in which option may appear */
|
|
};
|
|
|
|
/*
|
|
* Copied from fe-connect.c PQconninfoOptions.
|
|
*
|
|
* The list is small - don't bother with bsearch if it stays so.
|
|
*/
|
|
static const struct ConnectionOption libpq_conninfo_options[] = {{"authtype", ForeignServerRelationId},
|
|
{"service", ForeignServerRelationId},
|
|
{"user", UserMappingRelationId},
|
|
{"password", UserMappingRelationId},
|
|
{"connect_timeout", ForeignServerRelationId},
|
|
{"dbname", ForeignServerRelationId},
|
|
{"host", ForeignServerRelationId},
|
|
{"hostaddr", ForeignServerRelationId},
|
|
{"port", ForeignServerRelationId},
|
|
{"tty", ForeignServerRelationId},
|
|
{"options", ForeignServerRelationId},
|
|
{"requiressl", ForeignServerRelationId},
|
|
{"sslmode", ForeignServerRelationId},
|
|
{"gsslib", ForeignServerRelationId},
|
|
{NULL, InvalidOid}};
|
|
|
|
/*
|
|
* Check if the provided option is one of libpq conninfo options.
|
|
* context is the Oid of the catalog the option came from, or 0 if we
|
|
* don't care.
|
|
*/
|
|
static bool is_conninfo_option(const char* option, Oid context)
|
|
{
|
|
const struct ConnectionOption* opt = NULL;
|
|
|
|
for (opt = libpq_conninfo_options; opt->optname; opt++) {
|
|
if (context == opt->optcontext && strcmp(opt->optname, option) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Validate the generic option given to SERVER or USER MAPPING.
|
|
* Raise an ERROR if the option or its value is considered invalid.
|
|
*
|
|
* Valid server options are all libpq conninfo options except
|
|
* user and password -- these may only appear in USER MAPPING options.
|
|
*
|
|
* Caution: this function is deprecated, and is now meant only for testing
|
|
* purposes, because the list of options it knows about doesn't necessarily
|
|
* square with those known to whichever libpq instance you might be using.
|
|
* Inquire of libpq itself, instead.
|
|
*/
|
|
Datum postgresql_fdw_validator(PG_FUNCTION_ARGS)
|
|
{
|
|
List* options_list = untransformRelOptions(PG_GETARG_DATUM(0));
|
|
Oid catalog = PG_GETARG_OID(1);
|
|
|
|
ListCell* cell = NULL;
|
|
|
|
foreach (cell, options_list) {
|
|
DefElem* def = (DefElem*)lfirst(cell);
|
|
|
|
if (!is_conninfo_option(def->defname, catalog)) {
|
|
const struct ConnectionOption* opt = NULL;
|
|
StringInfoData buf;
|
|
|
|
/*
|
|
* Unknown option specified, complain about it. Provide a hint
|
|
* with list of valid options for the object.
|
|
*/
|
|
initStringInfo(&buf);
|
|
for (opt = libpq_conninfo_options; opt->optname; opt++) {
|
|
if (catalog == opt->optcontext) {
|
|
appendStringInfo(&buf, "%s%s", (buf.len > 0) ? ", " : "", opt->optname);
|
|
}
|
|
}
|
|
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FDW_INVALID_OPTION_NAME),
|
|
errmsg("invalid option \"%s\"", def->defname),
|
|
errhint("Valid options in this context are: %s", buf.data)));
|
|
|
|
PG_RETURN_BOOL(false);
|
|
}
|
|
}
|
|
|
|
PG_RETURN_BOOL(true);
|
|
}
|
|
|
|
/*
|
|
* get_foreign_data_wrapper_oid - given a FDW name, look up the OID
|
|
*
|
|
* If missing_ok is false, throw an error if name not found. If true, just
|
|
* return InvalidOid.
|
|
*/
|
|
Oid get_foreign_data_wrapper_oid(const char* fdwname, bool missing_ok)
|
|
{
|
|
Oid oid;
|
|
|
|
oid = GetSysCacheOid1(FOREIGNDATAWRAPPERNAME, CStringGetDatum(fdwname));
|
|
if (!OidIsValid(oid) && !missing_ok) {
|
|
ereport(
|
|
ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("foreign-data wrapper \"%s\" does not exist", fdwname)));
|
|
}
|
|
return oid;
|
|
}
|
|
|
|
/*
|
|
* get_foreign_server_oid - given a FDW name, look up the OID
|
|
*
|
|
* If missing_ok is false, throw an error if name not found. If true, just
|
|
* return InvalidOid.
|
|
*/
|
|
Oid get_foreign_server_oid(const char* servername, bool missing_ok)
|
|
{
|
|
Oid oid;
|
|
|
|
oid = GetSysCacheOid1(FOREIGNSERVERNAME, CStringGetDatum(servername));
|
|
if (!OidIsValid(oid) && !missing_ok) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("server \"%s\" does not exist", servername)));
|
|
}
|
|
return oid;
|
|
}
|
|
|
|
DefElem* GetForeignTableOptionByName(Oid reloid, const char* optname)
|
|
{
|
|
ForeignTable* table = NULL;
|
|
ForeignServer* server = NULL;
|
|
ForeignDataWrapper* wrapper = NULL;
|
|
List* options = NIL;
|
|
ListCell* lc = NULL;
|
|
|
|
/*
|
|
* Extract options from FDW objects. We ignore user mappings because
|
|
* file_fdw doesn't have any options that can be specified there.
|
|
*
|
|
* (XXX Actually, given the current contents of valid_options[], there's
|
|
* no point in examining anything except the foreign table's own options.
|
|
* Simplify?)
|
|
*/
|
|
table = GetForeignTable(reloid);
|
|
server = GetForeignServer(table->serverid);
|
|
wrapper = GetForeignDataWrapper(server->fdwid);
|
|
|
|
options = NIL;
|
|
options = list_concat(options, wrapper->options);
|
|
options = list_concat(options, server->options);
|
|
options = list_concat(options, table->options);
|
|
|
|
foreach (lc, options) {
|
|
DefElem* def = (DefElem*)lfirst(lc);
|
|
|
|
if (strcmp(def->defname, optname) == 0) {
|
|
return def;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
HdfsFdwOptions* HdfsGetOptions(Oid foreignTableId)
|
|
{
|
|
HdfsFdwOptions* hdfsFdwOptions = NULL;
|
|
char* address = NULL;
|
|
List* AddrList = NULL;
|
|
|
|
hdfsFdwOptions = (HdfsFdwOptions*)palloc0(sizeof(HdfsFdwOptions));
|
|
hdfsFdwOptions->filename = HdfsGetOptionValue(foreignTableId, OPTION_NAME_FILENAMES);
|
|
hdfsFdwOptions->foldername = HdfsGetOptionValue(foreignTableId, OPTION_NAME_FOLDERNAME);
|
|
hdfsFdwOptions->location = HdfsGetOptionValue(foreignTableId, OPTION_NAME_LOCATION);
|
|
|
|
address = HdfsGetOptionValue(foreignTableId, OPTION_NAME_ADDRESS);
|
|
|
|
if (NULL != address) {
|
|
if (T_HDFS_SERVER == getServerType(foreignTableId)) {
|
|
CheckGetServerIpAndPort(address, &AddrList, false, -1);
|
|
hdfsFdwOptions->address = ((HdfsServerAddress*)linitial(AddrList))->HdfsIp;
|
|
hdfsFdwOptions->port = atoi(((HdfsServerAddress*)linitial(AddrList))->HdfsPort);
|
|
} else {
|
|
/* As for OBS table, only the address is needed. */
|
|
hdfsFdwOptions->address = address;
|
|
}
|
|
}
|
|
|
|
return hdfsFdwOptions;
|
|
}
|
|
|
|
/*
|
|
* @Description: get option DefElem
|
|
* @IN foreignTableId: foreign table oid
|
|
* @IN optionName: option name
|
|
* @Return: DefElem ptr of option
|
|
* @See also:
|
|
*/
|
|
DefElem* HdfsGetOptionDefElem(Oid foreignTableId, const char* optionName)
|
|
{
|
|
List* optionList = NIL;
|
|
ListCell* optionCell = NULL;
|
|
DefElem* returnOptDef = NULL;
|
|
|
|
optionList = getFdwOptions(foreignTableId);
|
|
|
|
foreach (optionCell, optionList) {
|
|
DefElem* optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (strncmp(optionDefName, optionName, NAMEDATALEN) == 0) {
|
|
returnOptDef = (DefElem*)copyObject(optionDef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optionList != NIL) {
|
|
pfree(optionList);
|
|
}
|
|
|
|
return returnOptDef;
|
|
}
|
|
|
|
/* GetOption,GetOptionValue,GetOptionName and related structs need to be merged. */
|
|
char* HdfsGetOptionValue(Oid foreignTableId, const char* optionName)
|
|
{
|
|
#define DEST_CIPHER_LENGTH 1024
|
|
char* optionValue = NULL;
|
|
char decrypStr[DEST_CIPHER_LENGTH] = {'\0'};
|
|
errno_t rc = EOK;
|
|
|
|
DefElem* optionDef = HdfsGetOptionDefElem(foreignTableId, optionName);
|
|
|
|
if (optionDef != NULL) {
|
|
optionValue = defGetString(optionDef);
|
|
if (0 == pg_strcasecmp(optionName, OPTION_NAME_SERVER_SAK)) {
|
|
decryptKeyString(optionValue, decrypStr, DEST_CIPHER_LENGTH);
|
|
optionValue = pstrdup(decrypStr);
|
|
rc = memset_s(decrypStr, DEST_CIPHER_LENGTH, 0, DEST_CIPHER_LENGTH);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
}
|
|
|
|
return optionValue;
|
|
}
|
|
|
|
ObsOptions* getObsOptions(Oid foreignTableId)
|
|
{
|
|
#define LOCAL_STRING_BUFFER_SIZE 512
|
|
|
|
ObsOptions* obsOptions = NULL;
|
|
AutoContextSwitch newContext(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_STORAGE));
|
|
errno_t rc = EOK;
|
|
|
|
obsOptions = (ObsOptions*)palloc0(sizeof(ObsOptions));
|
|
|
|
if (IS_OBS_CSV_TXT_FOREIGN_TABLE(foreignTableId)) {
|
|
char buffer[LOCAL_STRING_BUFFER_SIZE];
|
|
int ibegin = 0;
|
|
int iend = 0;
|
|
int copylen = 0;
|
|
|
|
char* location = HdfsGetOptionValue(foreignTableId, optLocation);
|
|
ibegin = find_Nth(location, 2, "/");
|
|
iend = find_Nth(location, 3, "/");
|
|
copylen = iend - ibegin - 1;
|
|
rc = strncpy_s(buffer, LOCAL_STRING_BUFFER_SIZE, location + (ibegin + 1), copylen);
|
|
securec_check(rc, "", "");
|
|
obsOptions->address = pstrdup(buffer);
|
|
} else {
|
|
obsOptions->address = HdfsGetOptionValue(foreignTableId, OPTION_NAME_ADDRESS);
|
|
if (NULL == obsOptions->address) {
|
|
char* region = HdfsGetOptionValue(foreignTableId, OPTION_NAME_REGION);
|
|
obsOptions->address = readDataFromJsonFile(region);
|
|
}
|
|
}
|
|
|
|
DefElem* optionDef = HdfsGetOptionDefElem(foreignTableId, OPTION_NAME_SERVER_ENCRYPT);
|
|
if (optionDef == NULL) {
|
|
obsOptions->encrypt = false;
|
|
} else {
|
|
obsOptions->encrypt = defGetBoolean(optionDef);
|
|
}
|
|
|
|
obsOptions->access_key = HdfsGetOptionValue(foreignTableId, OPTION_NAME_SERVER_AK);
|
|
|
|
char* tempStr = HdfsGetOptionValue(foreignTableId, OPTION_NAME_SERVER_SAK);
|
|
if (tempStr != NULL) {
|
|
obsOptions->secret_access_key = (char*)SEC_encodeBase64(tempStr, strlen(tempStr));
|
|
rc = memset_s(tempStr, strlen(tempStr), 0, strlen(tempStr));
|
|
securec_check(rc, "\0", "\0");
|
|
pfree(tempStr);
|
|
}
|
|
|
|
obsOptions->bucket = NULL;
|
|
|
|
obsOptions->prefix = NULL;
|
|
|
|
return obsOptions;
|
|
}
|
|
|
|
/**
|
|
* @Description: get the foreign server type of the special foreign talbe..
|
|
* @in foreignTableId, the given foreign table oid.
|
|
* @return
|
|
* notes: currently, only the hdfs foreign table and obs foreign table have
|
|
* server type. so when the given foreign table is not either of obs foreign
|
|
* foreign table and obs foreign table, return T_INVALID.
|
|
*/
|
|
ServerTypeOption getServerType(Oid foreignTableId)
|
|
{
|
|
char* optionValue = HdfsGetOptionValue(foreignTableId, "type");
|
|
ServerTypeOption srvType = T_INVALID;
|
|
|
|
if (optionValue != NULL) {
|
|
if (0 == pg_strcasecmp(optionValue, OBS_SERVER)) {
|
|
srvType = T_OBS_SERVER;
|
|
} else if (0 == pg_strcasecmp(optionValue, HDFS_SERVER)) {
|
|
srvType = T_HDFS_SERVER;
|
|
} else if (0 == pg_strcasecmp(optionValue, DUMMY_SERVER)) {
|
|
srvType = T_DUMMY_SERVER;
|
|
}
|
|
} else if (IsSpecifiedFDWFromRelid(foreignTableId, DIST_FDW) &&
|
|
(is_obs_protocol(HdfsGetOptionValue(foreignTableId, optLocation)))) {
|
|
srvType = T_TXT_CSV_OBS_SERVER;
|
|
} else if (IsSpecifiedFDWFromRelid(foreignTableId, GC_FDW)) {
|
|
srvType = T_PGFDW_SERVER;
|
|
}
|
|
|
|
/* for in-place upgrade */
|
|
if (T_INVALID == srvType && IsSpecifiedFDWFromRelid(foreignTableId, HDFS_FDW)) {
|
|
srvType = T_HDFS_SERVER;
|
|
}
|
|
|
|
return srvType;
|
|
}
|
|
|
|
/**
|
|
* @Description: get the all option by the given foreign table oid,
|
|
* which include foreign talbe option
|
|
* and foreign server option.
|
|
* @in foreignTableId, the foreign table oid.
|
|
* @return return all options.
|
|
*/
|
|
List* getFdwOptions(Oid foreignTableId)
|
|
{
|
|
List* optionList = NIL;
|
|
ForeignTable* foreignTbl = NULL;
|
|
ForeignServer* foreignSrv = NULL;
|
|
|
|
foreignTbl = GetForeignTable(foreignTableId);
|
|
foreignSrv = GetForeignServer(foreignTbl->serverid);
|
|
|
|
optionList = list_concat(optionList, foreignTbl->options);
|
|
optionList = list_concat(optionList, foreignSrv->options);
|
|
|
|
return optionList;
|
|
}
|
|
|
|
/**
|
|
* @Description: get one server option value by special option name.
|
|
* server oid.
|
|
* @in srvOid, the given foreign server option.
|
|
* @in optionName, the server option name.
|
|
* @out
|
|
* @out
|
|
* @return
|
|
*/
|
|
char* getServerOptionValue(Oid srvOid, const char* optionName)
|
|
{
|
|
#define DEST_CIPHER_LENGTH 1024
|
|
|
|
ForeignServer* foreignSrv = GetForeignServer(srvOid);
|
|
List* optionList = foreignSrv->options;
|
|
char* optionValue = NULL;
|
|
ListCell* optionCell = NULL;
|
|
char decrypStr[DEST_CIPHER_LENGTH] = {'\0'};
|
|
|
|
foreach (optionCell, optionList) {
|
|
DefElem* optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (strncmp(optionDefName, optionName, NAMEDATALEN) == 0) {
|
|
optionValue = defGetString(optionDef);
|
|
|
|
if (0 == pg_strcasecmp(optionName, OPTION_NAME_PASSWD)) {
|
|
decryptKeyString(optionValue, decrypStr, DEST_CIPHER_LENGTH);
|
|
optionValue = pstrdup(decrypStr);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return optionValue;
|
|
}
|
|
|
|
/**
|
|
* @Description: count the number of the given specialChar
|
|
* in string "path".
|
|
* @in path, find special character in this string.
|
|
* @in specialChar, the given character.
|
|
* @return return the statistic.
|
|
*/
|
|
int getSpecialCharCnt(const char* path, const char specialChar)
|
|
{
|
|
int specialCharNum = 0;
|
|
|
|
Assert(NULL != path);
|
|
|
|
size_t pathLen = strlen(path);
|
|
|
|
for (size_t i = 0; i < pathLen; i++) {
|
|
if (path[i] == specialChar) {
|
|
specialCharNum++;
|
|
}
|
|
}
|
|
|
|
return specialCharNum;
|
|
}
|
|
|
|
/**
|
|
* @Description: get the specified option from the option list.
|
|
* @in optionList, the given optionList.
|
|
* @in optionName, the specified option name.
|
|
* @return retue the option value using the DefElem struct.
|
|
*/
|
|
DefElem* getFTOptionDefElemFromList(List* optionList, const char* optionName)
|
|
{
|
|
ListCell* optionCell = NULL;
|
|
DefElem* def = NULL;
|
|
foreach (optionCell, optionList) {
|
|
DefElem* optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (strncmp(optionDefName, optionName, NAMEDATALEN) == 0) {
|
|
def = (DefElem*)copyObject(optionDef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return def;
|
|
}
|
|
|
|
/**
|
|
* @Description: get value assocaited with option
|
|
* @List* optionList: input option list
|
|
* @char* optionName: input option name
|
|
* @return char* optionValue: output option value
|
|
*/
|
|
char* getFTOptionValue(List* optionList, const char* optionName)
|
|
{
|
|
char* optionValue = NULL;
|
|
ListCell* optionCell = NULL;
|
|
foreach (optionCell, optionList) {
|
|
DefElem* optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (0 == pg_strcasecmp(optionDefName, optionName)) {
|
|
optionValue = defGetString(optionDef);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return optionValue;
|
|
}
|
|
|
|
/**
|
|
* @Description: get alter action assocaited with option
|
|
* @List* optionList: input option list
|
|
* @char* optionName: input option name
|
|
* @return DefElemAction alterAction: output alter action
|
|
*/
|
|
DefElemAction getFTAlterAction(List* optionList, const char* optionName)
|
|
{
|
|
DefElemAction alterAction = DEFELEM_UNSPEC;
|
|
ListCell* optionCell = NULL;
|
|
foreach (optionCell, optionList) {
|
|
DefElem* optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (pg_strcasecmp(optionDefName, optionName) == 0) {
|
|
alterAction = optionDef->defaction;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return alterAction;
|
|
}
|
|
|
|
/**
|
|
* @Description: validate sting and convert it to double
|
|
* @char* s_value: input string
|
|
* @return double if input string is valid
|
|
*/
|
|
double convertFTOptionValue(const char* s_value)
|
|
{
|
|
int64 l_value = 0;
|
|
bool cvtd = scanint8(s_value, true, &l_value);
|
|
|
|
if ((!cvtd) || (l_value < MIN_TOTALROWS)) {
|
|
ereport(ERROR, (errcode(ERRCODE_FDW_ERROR), errmsg("totalrows is invalid")));
|
|
}
|
|
|
|
return (double)l_value;
|
|
}
|
|
|
|
ObsOptions* setObsSrvOptions(ForeignOptions* fOptions)
|
|
{
|
|
#define DEST_CIPHER_LENGTH 1024
|
|
|
|
ObsOptions* options = (ObsOptions*)palloc0(sizeof(ObsOptions));
|
|
char* address = getFTOptionValue(fOptions->fOptions, OPTION_NAME_ADDRESS);
|
|
errno_t rc = EOK;
|
|
|
|
if (NULL == address) {
|
|
char* region = getFTOptionValue(fOptions->fOptions, OPTION_NAME_REGION);
|
|
options->address = readDataFromJsonFile(region);
|
|
} else {
|
|
options->address = address;
|
|
}
|
|
|
|
options->access_key = getFTOptionValue(fOptions->fOptions, OPTION_NAME_SERVER_AK);
|
|
|
|
DefElem* def_elem = getFTOptionDefElemFromList(fOptions->fOptions, OPTION_NAME_SERVER_ENCRYPT);
|
|
options->encrypt = def_elem ? defGetBoolean(def_elem) : false;
|
|
char* sak = getFTOptionValue(fOptions->fOptions, OPTION_NAME_SERVER_SAK);
|
|
|
|
char* obskey = getFTOptionValue(fOptions->fOptions, OPTION_NAME_OBSKEY);
|
|
if (NULL == obskey) {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_ACCELERATE),
|
|
errcode(ERRCODE_FDW_ERROR),
|
|
errmsg("Failed to get obskey from options of DWS.")));
|
|
}
|
|
|
|
char decrypStr[DEST_CIPHER_LENGTH] = {'\0'};
|
|
decryptKeyString(sak, decrypStr, DEST_CIPHER_LENGTH, obskey);
|
|
options->secret_access_key = pstrdup(decrypStr);
|
|
|
|
rc = memset_s(decrypStr, DEST_CIPHER_LENGTH, 0, DEST_CIPHER_LENGTH);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
return options;
|
|
}
|
|
|
|
HdfsOptions* setHdfsSrvOptions(ForeignOptions* fOptions)
|
|
{
|
|
HdfsOptions* options = (HdfsOptions*)palloc0(sizeof(HdfsOptions));
|
|
|
|
options->servername = getFTOptionValue(fOptions->fOptions, OPTION_NAME_REMOTESERVERNAME);
|
|
|
|
options->format = getFTOptionValue(fOptions->fOptions, OPTION_NAME_FORMAT);
|
|
if (NULL == options->format) {
|
|
ereport(ERROR, (errcode(ERRCODE_FDW_ERROR), errmsg("No \"format\" option provided.")));
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
extern char* TrimStr(const char* str);
|
|
|
|
/**
|
|
* @Description: Currently, two OBS location format support in database.
|
|
* one format is "gsobs:://obsdomain/bucket/prefix", another is "gsobs:://bucket.obsdomain/prefix".
|
|
* we adjust second format to the first format, we only deal with the first format in parse phase.
|
|
* @in optionsList, if the list include location option, we will adjust format.
|
|
* @return return new optionsList.
|
|
*/
|
|
List* regularizeObsLocationInfo(List* optionsList)
|
|
{
|
|
char* sourceStr = NULL;
|
|
ListCell* optionCell = NULL;
|
|
DefElem* optionDef = NULL;
|
|
char* returnLocation = NULL;
|
|
char* token = NULL;
|
|
char* saved = NULL;
|
|
char* cpyLocation = NULL;
|
|
char* str = NULL;
|
|
size_t startPos = 0;
|
|
errno_t rc;
|
|
DefElem* locationDefElem = NULL;
|
|
|
|
foreach (optionCell, optionsList) {
|
|
optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (0 == pg_strcasecmp(optionDefName, optLocation)) {
|
|
|
|
if (DEFELEM_DROP == optionDef->defaction) {
|
|
/*
|
|
* if the user commond is that "alter foreign table ft options(drop location)"
|
|
* the optionDef has no value, so we retrun it.
|
|
*/
|
|
} else {
|
|
/* found it. */
|
|
sourceStr = defGetString(optionDef);
|
|
|
|
locationDefElem = (DefElem*)copyObject(optionDef);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* if we do not found this option, return it. */
|
|
if (NULL == sourceStr || 0 == strlen(sourceStr) || !is_obs_protocol(sourceStr)) {
|
|
return optionsList;
|
|
}
|
|
|
|
optionsList = list_delete(optionsList, optionDef);
|
|
|
|
returnLocation = (char*)palloc0(sizeof(char) * (strlen(sourceStr) + 1));
|
|
|
|
cpyLocation = pstrdup(sourceStr);
|
|
token = strtok_r(cpyLocation, "|", &saved);
|
|
|
|
while (NULL != token) {
|
|
char* tempStr = NULL;
|
|
str = TrimStr(token);
|
|
if (NULL == str) {
|
|
ereport(ERROR, (errcode(ERRCODE_FDW_ERROR), errmsg("Invalid URL \"%s\" in LOCATION", token)));
|
|
}
|
|
tempStr = adjustObsLocationInfoOrder(str);
|
|
|
|
rc = memcpy_s(returnLocation + startPos, strlen(tempStr), tempStr, strlen(tempStr));
|
|
securec_check(rc, "\0", "\0");
|
|
startPos += strlen(tempStr);
|
|
pfree(tempStr);
|
|
|
|
token = strtok_r(NULL, "|", &saved);
|
|
|
|
if (NULL != token) {
|
|
rc = memcpy_s(returnLocation + startPos, strlen("|"), "|", strlen("|"));
|
|
securec_check(rc, "\0", "\0");
|
|
startPos += strlen("|");
|
|
}
|
|
}
|
|
|
|
pfree(cpyLocation);
|
|
|
|
locationDefElem->arg = (Node*)makeString(returnLocation);
|
|
optionsList = lappend(optionsList, locationDefElem);
|
|
|
|
return optionsList;
|
|
}
|
|
|
|
/**
|
|
* @Description: Currently, two OBS location format support in database.
|
|
* one format is "gsobs:://obsdomain/bucket/prefix", another is "gsobs:://bucket.obsdomain/prefix".
|
|
* we adjust second format to the first format, we only deal with the first format in parse phase.
|
|
* @in sourceStr, if the given string includes GSOBS_PREFIX , we will adjust format.
|
|
* @return return new location string.
|
|
*/
|
|
char* adjustObsLocationInfoOrder(char* sourceStr)
|
|
{
|
|
size_t strLen = strlen(sourceStr);
|
|
char* newStr = NULL;
|
|
errno_t rc;
|
|
if (pg_strncasecmp(sourceStr, GSOBS_PREFIX, strlen(GSOBS_PREFIX))) {
|
|
/* it means that the option is not obs option. */
|
|
return sourceStr;
|
|
}
|
|
|
|
/*
|
|
* if we do not found the slash '/', it means that the option value is
|
|
* invalid, but we do not deal with it here, return it.
|
|
*/
|
|
char* prefixPos = strchr(sourceStr + strlen(GSOBS_PREFIX), '/');
|
|
if (NULL == prefixPos) {
|
|
return sourceStr;
|
|
}
|
|
|
|
/* it store the string "bucket.obsdomain", for example, bucket.obsdomain/prefix. */
|
|
size_t tempStrLen = (prefixPos - sourceStr) - strlen(GSOBS_PREFIX) + 1;
|
|
char* tempStr = (char*)palloc0(sizeof(char) * tempStrLen);
|
|
rc = memcpy_s(tempStr, tempStrLen, sourceStr + strlen(GSOBS_PREFIX), tempStrLen - 1);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
if (NULL != strstr(tempStr, OBS_BUCKET_URL_FORMAT_FLAG)) {
|
|
/*
|
|
* if we found the OBS_BUCKET_URL_FORMAT_FLAG in tempStr.
|
|
* we assume that location string format is the "bucket.obsdomain/prefix" form.
|
|
*/
|
|
/* found the last subStr OBS_BUCKET_URL_FORMAT_FLAG. */
|
|
char* urlStart = pg_strrstr(tempStr, OBS_BUCKET_URL_FORMAT_FLAG);
|
|
|
|
newStr = (char*)palloc0(sizeof(char) * strLen + 1);
|
|
/* copy the GSOBS_PREFIX string. */
|
|
rc = memcpy_s(newStr, strLen, GSOBS_PREFIX, strlen(GSOBS_PREFIX));
|
|
securec_check(rc, "\0", "\0");
|
|
strLen -= strlen(GSOBS_PREFIX);
|
|
|
|
/* append url string. */
|
|
rc = memcpy_s(newStr + strlen(GSOBS_PREFIX), strLen, urlStart + 1, strlen(urlStart) - 1);
|
|
securec_check(rc, "\0", "\0");
|
|
strLen -= strlen(urlStart) - 1;
|
|
|
|
/* append one char '/'. */
|
|
rc = memcpy_s(newStr + strlen(GSOBS_PREFIX) + strlen(urlStart) - 1, strLen, "/", strlen("/"));
|
|
securec_check(rc, "\0", "\0");
|
|
strLen -= strlen("/");
|
|
|
|
/* append bucket string. */
|
|
rc = memcpy_s(newStr + strlen(GSOBS_PREFIX) + strlen(urlStart) - 1 + 1, strLen, tempStr, urlStart - tempStr);
|
|
securec_check(rc, "\0", "\0");
|
|
strLen -= urlStart - tempStr;
|
|
|
|
/* append prefix string. */
|
|
rc = memcpy_s(newStr + strlen(GSOBS_PREFIX) + strlen(urlStart) + (urlStart - tempStr),
|
|
strLen,
|
|
prefixPos,
|
|
strlen(prefixPos));
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
|
|
pfree(tempStr);
|
|
|
|
return newStr ? newStr : sourceStr;
|
|
}
|
|
|
|
/**
|
|
* @Description: find the given sub string in source string, mark the postion
|
|
* on which appear the sub string in last time.
|
|
* @in string, the source string.
|
|
* @in subStr, the subStr to be found.
|
|
* @return if found it return the possition, otherwise return NULL.
|
|
*/
|
|
char* pg_strrstr(char* string, const char* subStr)
|
|
{
|
|
char* index = NULL;
|
|
char* ret = NULL;
|
|
int i = 0;
|
|
|
|
do {
|
|
index = strstr(string + i++, subStr);
|
|
if (NULL != index) {
|
|
ret = index;
|
|
}
|
|
|
|
} while (NULL != index);
|
|
|
|
return ret;
|
|
}
|
|
|
|
List* adaptOBSURL(List* optionList)
|
|
{
|
|
ListCell* optionCell = NULL;
|
|
char* regionCode = NULL;
|
|
Value* locationValue = NULL;
|
|
foreach (optionCell, optionList) {
|
|
DefElem* optionDef = (DefElem*)lfirst(optionCell);
|
|
char* optionDefName = optionDef->defname;
|
|
|
|
if (pg_strcasecmp(optionDefName, OPTION_NAME_REGION) == 0) {
|
|
regionCode = defGetString(optionDef);
|
|
}
|
|
|
|
if (pg_strcasecmp(optionDefName, OPTION_NAME_LOCATION) == 0) {
|
|
locationValue = (Value*)(optionDef->arg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Fill the location option. As for the gsobs:// prefix, we do not fill
|
|
* the location.
|
|
*/
|
|
if (NULL != locationValue && 0 == pg_strncasecmp(strVal(locationValue), OBS_PREFIX, OBS_PREfIX_LEN)) {
|
|
/* the regionCode may be NULL, we will get the default region. */
|
|
char* newLocation = rebuildAllLocationOptions(regionCode, strVal(locationValue));
|
|
strVal(locationValue) = newLocation;
|
|
}
|
|
|
|
return optionList;
|
|
}
|
|
|
|
/**
|
|
* @Description: rebuild the each location option for the obs import/export foregin table.
|
|
* we will "hostname/region" fill the fornt end of each location.
|
|
* @in regionCode, it may be an empty pointor.
|
|
* @return return the filled location.
|
|
*/
|
|
char* rebuildAllLocationOptions(char* regionCode, char* location)
|
|
{
|
|
size_t len = location ? strlen(location) : 0;
|
|
char* str = NULL;
|
|
char* token = NULL;
|
|
char* saved = NULL;
|
|
StringInfo retOptions = makeStringInfo();
|
|
|
|
if (0 == len) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FDW_ERROR), errmodule(MOD_DFS), errmsg("Invalid location value for the foreign table.")));
|
|
}
|
|
|
|
token = strtok_r(location, "|", &saved);
|
|
while (NULL != token) {
|
|
str = TrimStr(token);
|
|
if (NULL != str) {
|
|
appendStringInfo(retOptions, "%s", rebuildLocationOption(regionCode, str));
|
|
}
|
|
token = strtok_r(NULL, "|", &saved);
|
|
if (NULL != token) {
|
|
appendStringInfo(retOptions, "|");
|
|
}
|
|
}
|
|
ereport(DEBUG1,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmodule(MOD_DFS),
|
|
errmsg("The rebuild all region string: %s.", retOptions->data)));
|
|
|
|
return retOptions->data;
|
|
}
|
|
|
|
/**
|
|
* @Description: fill the region string to the front end of the given location.
|
|
* get the region string from the readDataFromJsonFile function.
|
|
* @in regionCode, the given regionCode.
|
|
* @in location, the given location.
|
|
* @return return the filled location.
|
|
*/
|
|
char* rebuildLocationOption(char* regionCode, char* location)
|
|
{
|
|
char* region = readDataFromJsonFile(regionCode);
|
|
char* rebuildLocation = NULL;
|
|
size_t rebuildLen = strlen(region) + strlen(location) + 2;
|
|
|
|
rebuildLocation = (char*)palloc0(rebuildLen * (sizeof(char)));
|
|
|
|
ereport(DEBUG1,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmodule(MOD_DFS), errmsg("The location string: %s.", location)));
|
|
|
|
ereport(DEBUG1,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmodule(MOD_DFS), errmsg("The region string: %s.", region)));
|
|
|
|
/* find the position of starting bucket. */
|
|
int pos = find_Nth(location, 2, "/") + 1;
|
|
|
|
int errorno = 0;
|
|
|
|
errorno = strncpy_s(rebuildLocation, rebuildLen, OBS_PREFIX, OBS_PREfIX_LEN + 1);
|
|
securec_check(errorno, "\0", "\0");
|
|
rebuildLocation[OBS_PREfIX_LEN] = '\0';
|
|
|
|
errorno = strncpy_s(rebuildLocation + OBS_PREfIX_LEN, rebuildLen, region, strlen(region) + 1);
|
|
securec_check(errorno, "\0", "\0");
|
|
rebuildLocation[OBS_PREfIX_LEN + strlen(region)] = '\0';
|
|
|
|
errorno = strncpy_s(rebuildLocation + OBS_PREfIX_LEN + strlen(region), rebuildLen, "/", 1 + 1);
|
|
securec_check(errorno, "\0", "\0");
|
|
rebuildLocation[OBS_PREfIX_LEN + strlen(region) + 1] = '\0';
|
|
|
|
errorno = strncpy_s(
|
|
rebuildLocation + OBS_PREfIX_LEN + strlen(region) + 1, rebuildLen, location + pos, strlen(location + pos) + 1);
|
|
securec_check(errorno, "\0", "\0");
|
|
rebuildLocation[OBS_PREfIX_LEN + strlen(region) + strlen(location + pos) + 1] = '\0';
|
|
|
|
ereport(DEBUG1,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmodule(MOD_DFS),
|
|
errmsg("The rebuild region string: %s.", rebuildLocation)));
|
|
|
|
return rebuildLocation;
|
|
}
|