mirror of
https://github.com/postgres/postgres.git
synced 2025-07-27 12:41:57 +03:00
Run pgindent on the changes of the previous patch.
This step can be checked mechanically. Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Matheus Alcantara <matheusssilv97@gmail.com> Discussion: https://postgr.es/m/2976982.1748049023@sss.pgh.pa.us
This commit is contained in:
@ -870,115 +870,115 @@ materializeResult(FunctionCallInfo fcinfo, PGconn *conn, PGresult *res)
|
|||||||
/* prepTuplestoreResult must have been called previously */
|
/* prepTuplestoreResult must have been called previously */
|
||||||
Assert(rsinfo->returnMode == SFRM_Materialize);
|
Assert(rsinfo->returnMode == SFRM_Materialize);
|
||||||
|
|
||||||
if (PQresultStatus(res) == PGRES_COMMAND_OK)
|
if (PQresultStatus(res) == PGRES_COMMAND_OK)
|
||||||
{
|
{
|
||||||
is_sql_cmd = true;
|
is_sql_cmd = true;
|
||||||
|
|
||||||
/*
|
|
||||||
* need a tuple descriptor representing one TEXT column to return
|
|
||||||
* the command status string as our result tuple
|
|
||||||
*/
|
|
||||||
tupdesc = CreateTemplateTupleDesc(1);
|
|
||||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
|
|
||||||
TEXTOID, -1, 0);
|
|
||||||
ntuples = 1;
|
|
||||||
nfields = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert(PQresultStatus(res) == PGRES_TUPLES_OK);
|
|
||||||
|
|
||||||
is_sql_cmd = false;
|
|
||||||
|
|
||||||
/* get a tuple descriptor for our result type */
|
|
||||||
switch (get_call_result_type(fcinfo, NULL, &tupdesc))
|
|
||||||
{
|
|
||||||
case TYPEFUNC_COMPOSITE:
|
|
||||||
/* success */
|
|
||||||
break;
|
|
||||||
case TYPEFUNC_RECORD:
|
|
||||||
/* failed to determine actual type of RECORD */
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
||||||
errmsg("function returning record called in context "
|
|
||||||
"that cannot accept type record")));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* result type isn't composite */
|
|
||||||
elog(ERROR, "return type must be a row type");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* make sure we have a persistent copy of the tupdesc */
|
|
||||||
tupdesc = CreateTupleDescCopy(tupdesc);
|
|
||||||
ntuples = PQntuples(res);
|
|
||||||
nfields = PQnfields(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* check result and tuple descriptor have the same number of columns
|
* need a tuple descriptor representing one TEXT column to return the
|
||||||
|
* command status string as our result tuple
|
||||||
*/
|
*/
|
||||||
if (nfields != tupdesc->natts)
|
tupdesc = CreateTemplateTupleDesc(1);
|
||||||
ereport(ERROR,
|
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "status",
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
TEXTOID, -1, 0);
|
||||||
errmsg("remote query result rowtype does not match "
|
ntuples = 1;
|
||||||
"the specified FROM clause rowtype")));
|
nfields = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Assert(PQresultStatus(res) == PGRES_TUPLES_OK);
|
||||||
|
|
||||||
if (ntuples > 0)
|
is_sql_cmd = false;
|
||||||
|
|
||||||
|
/* get a tuple descriptor for our result type */
|
||||||
|
switch (get_call_result_type(fcinfo, NULL, &tupdesc))
|
||||||
{
|
{
|
||||||
AttInMetadata *attinmeta;
|
case TYPEFUNC_COMPOSITE:
|
||||||
int nestlevel = -1;
|
/* success */
|
||||||
Tuplestorestate *tupstore;
|
break;
|
||||||
MemoryContext oldcontext;
|
case TYPEFUNC_RECORD:
|
||||||
int row;
|
/* failed to determine actual type of RECORD */
|
||||||
char **values;
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
errmsg("function returning record called in context "
|
||||||
|
"that cannot accept type record")));
|
||||||
/* Set GUCs to ensure we read GUC-sensitive data types correctly */
|
break;
|
||||||
if (!is_sql_cmd)
|
default:
|
||||||
nestlevel = applyRemoteGucs(conn);
|
/* result type isn't composite */
|
||||||
|
elog(ERROR, "return type must be a row type");
|
||||||
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
break;
|
||||||
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
|
||||||
rsinfo->setResult = tupstore;
|
|
||||||
rsinfo->setDesc = tupdesc;
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
|
||||||
|
|
||||||
values = palloc_array(char *, nfields);
|
|
||||||
|
|
||||||
/* put all tuples into the tuplestore */
|
|
||||||
for (row = 0; row < ntuples; row++)
|
|
||||||
{
|
|
||||||
HeapTuple tuple;
|
|
||||||
|
|
||||||
if (!is_sql_cmd)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < nfields; i++)
|
|
||||||
{
|
|
||||||
if (PQgetisnull(res, row, i))
|
|
||||||
values[i] = NULL;
|
|
||||||
else
|
|
||||||
values[i] = PQgetvalue(res, row, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
values[0] = PQcmdStatus(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* build the tuple and put it into the tuplestore. */
|
|
||||||
tuple = BuildTupleFromCStrings(attinmeta, values);
|
|
||||||
tuplestore_puttuple(tupstore, tuple);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clean up GUC settings, if we changed any */
|
|
||||||
restoreLocalGucs(nestlevel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PQclear(res);
|
/* make sure we have a persistent copy of the tupdesc */
|
||||||
|
tupdesc = CreateTupleDescCopy(tupdesc);
|
||||||
|
ntuples = PQntuples(res);
|
||||||
|
nfields = PQnfields(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check result and tuple descriptor have the same number of columns
|
||||||
|
*/
|
||||||
|
if (nfields != tupdesc->natts)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("remote query result rowtype does not match "
|
||||||
|
"the specified FROM clause rowtype")));
|
||||||
|
|
||||||
|
if (ntuples > 0)
|
||||||
|
{
|
||||||
|
AttInMetadata *attinmeta;
|
||||||
|
int nestlevel = -1;
|
||||||
|
Tuplestorestate *tupstore;
|
||||||
|
MemoryContext oldcontext;
|
||||||
|
int row;
|
||||||
|
char **values;
|
||||||
|
|
||||||
|
attinmeta = TupleDescGetAttInMetadata(tupdesc);
|
||||||
|
|
||||||
|
/* Set GUCs to ensure we read GUC-sensitive data types correctly */
|
||||||
|
if (!is_sql_cmd)
|
||||||
|
nestlevel = applyRemoteGucs(conn);
|
||||||
|
|
||||||
|
oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
|
||||||
|
tupstore = tuplestore_begin_heap(true, false, work_mem);
|
||||||
|
rsinfo->setResult = tupstore;
|
||||||
|
rsinfo->setDesc = tupdesc;
|
||||||
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
|
||||||
|
values = palloc_array(char *, nfields);
|
||||||
|
|
||||||
|
/* put all tuples into the tuplestore */
|
||||||
|
for (row = 0; row < ntuples; row++)
|
||||||
|
{
|
||||||
|
HeapTuple tuple;
|
||||||
|
|
||||||
|
if (!is_sql_cmd)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < nfields; i++)
|
||||||
|
{
|
||||||
|
if (PQgetisnull(res, row, i))
|
||||||
|
values[i] = NULL;
|
||||||
|
else
|
||||||
|
values[i] = PQgetvalue(res, row, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
values[0] = PQcmdStatus(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the tuple and put it into the tuplestore. */
|
||||||
|
tuple = BuildTupleFromCStrings(attinmeta, values);
|
||||||
|
tuplestore_puttuple(tupstore, tuple);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clean up GUC settings, if we changed any */
|
||||||
|
restoreLocalGucs(nestlevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -982,40 +982,40 @@ void
|
|||||||
pgfdw_report_error(int elevel, PGresult *res, PGconn *conn,
|
pgfdw_report_error(int elevel, PGresult *res, PGconn *conn,
|
||||||
const char *sql)
|
const char *sql)
|
||||||
{
|
{
|
||||||
char *diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
char *diag_sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
|
||||||
char *message_primary = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
|
char *message_primary = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
|
||||||
char *message_detail = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
|
char *message_detail = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL);
|
||||||
char *message_hint = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
|
char *message_hint = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT);
|
||||||
char *message_context = PQresultErrorField(res, PG_DIAG_CONTEXT);
|
char *message_context = PQresultErrorField(res, PG_DIAG_CONTEXT);
|
||||||
int sqlstate;
|
int sqlstate;
|
||||||
|
|
||||||
if (diag_sqlstate)
|
if (diag_sqlstate)
|
||||||
sqlstate = MAKE_SQLSTATE(diag_sqlstate[0],
|
sqlstate = MAKE_SQLSTATE(diag_sqlstate[0],
|
||||||
diag_sqlstate[1],
|
diag_sqlstate[1],
|
||||||
diag_sqlstate[2],
|
diag_sqlstate[2],
|
||||||
diag_sqlstate[3],
|
diag_sqlstate[3],
|
||||||
diag_sqlstate[4]);
|
diag_sqlstate[4]);
|
||||||
else
|
else
|
||||||
sqlstate = ERRCODE_CONNECTION_FAILURE;
|
sqlstate = ERRCODE_CONNECTION_FAILURE;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we don't get a message from the PGresult, try the PGconn. This
|
* If we don't get a message from the PGresult, try the PGconn. This is
|
||||||
* is needed because for connection-level failures, PQgetResult may
|
* needed because for connection-level failures, PQgetResult may just
|
||||||
* just return NULL, not a PGresult at all.
|
* return NULL, not a PGresult at all.
|
||||||
*/
|
*/
|
||||||
if (message_primary == NULL)
|
if (message_primary == NULL)
|
||||||
message_primary = pchomp(PQerrorMessage(conn));
|
message_primary = pchomp(PQerrorMessage(conn));
|
||||||
|
|
||||||
ereport(elevel,
|
ereport(elevel,
|
||||||
(errcode(sqlstate),
|
(errcode(sqlstate),
|
||||||
(message_primary != NULL && message_primary[0] != '\0') ?
|
(message_primary != NULL && message_primary[0] != '\0') ?
|
||||||
errmsg_internal("%s", message_primary) :
|
errmsg_internal("%s", message_primary) :
|
||||||
errmsg("could not obtain message string for remote error"),
|
errmsg("could not obtain message string for remote error"),
|
||||||
message_detail ? errdetail_internal("%s", message_detail) : 0,
|
message_detail ? errdetail_internal("%s", message_detail) : 0,
|
||||||
message_hint ? errhint("%s", message_hint) : 0,
|
message_hint ? errhint("%s", message_hint) : 0,
|
||||||
message_context ? errcontext("%s", message_context) : 0,
|
message_context ? errcontext("%s", message_context) : 0,
|
||||||
sql ? errcontext("remote SQL command: %s", sql) : 0));
|
sql ? errcontext("remote SQL command: %s", sql) : 0));
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1617,83 +1617,83 @@ pgfdw_get_cleanup_result(PGconn *conn, TimestampTz endtime,
|
|||||||
|
|
||||||
*result = NULL;
|
*result = NULL;
|
||||||
*timed_out = false;
|
*timed_out = false;
|
||||||
for (;;)
|
for (;;)
|
||||||
|
{
|
||||||
|
PGresult *res;
|
||||||
|
|
||||||
|
while (PQisBusy(conn))
|
||||||
{
|
{
|
||||||
PGresult *res;
|
int wc;
|
||||||
|
TimestampTz now = GetCurrentTimestamp();
|
||||||
|
long cur_timeout;
|
||||||
|
|
||||||
while (PQisBusy(conn))
|
/* If timeout has expired, give up. */
|
||||||
|
if (now >= endtime)
|
||||||
{
|
{
|
||||||
int wc;
|
*timed_out = true;
|
||||||
TimestampTz now = GetCurrentTimestamp();
|
failed = true;
|
||||||
long cur_timeout;
|
goto exit;
|
||||||
|
|
||||||
/* If timeout has expired, give up. */
|
|
||||||
if (now >= endtime)
|
|
||||||
{
|
|
||||||
*timed_out = true;
|
|
||||||
failed = true;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we need to re-issue the cancel request, do that. */
|
|
||||||
if (now >= retrycanceltime)
|
|
||||||
{
|
|
||||||
/* We ignore failure to issue the repeated request. */
|
|
||||||
(void) libpqsrv_cancel(conn, endtime);
|
|
||||||
|
|
||||||
/* Recompute "now" in case that took measurable time. */
|
|
||||||
now = GetCurrentTimestamp();
|
|
||||||
|
|
||||||
/* Adjust re-cancel timeout in increasing steps. */
|
|
||||||
retrycanceltime = TimestampTzPlusMilliseconds(now,
|
|
||||||
canceldelta);
|
|
||||||
canceldelta += canceldelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If timeout has expired, give up, else get sleep time. */
|
|
||||||
cur_timeout = TimestampDifferenceMilliseconds(now,
|
|
||||||
Min(endtime,
|
|
||||||
retrycanceltime));
|
|
||||||
if (cur_timeout <= 0)
|
|
||||||
{
|
|
||||||
*timed_out = true;
|
|
||||||
failed = true;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* first time, allocate or get the custom wait event */
|
|
||||||
if (pgfdw_we_cleanup_result == 0)
|
|
||||||
pgfdw_we_cleanup_result = WaitEventExtensionNew("PostgresFdwCleanupResult");
|
|
||||||
|
|
||||||
/* Sleep until there's something to do */
|
|
||||||
wc = WaitLatchOrSocket(MyLatch,
|
|
||||||
WL_LATCH_SET | WL_SOCKET_READABLE |
|
|
||||||
WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
|
|
||||||
PQsocket(conn),
|
|
||||||
cur_timeout, pgfdw_we_cleanup_result);
|
|
||||||
ResetLatch(MyLatch);
|
|
||||||
|
|
||||||
CHECK_FOR_INTERRUPTS();
|
|
||||||
|
|
||||||
/* Data available in socket? */
|
|
||||||
if (wc & WL_SOCKET_READABLE)
|
|
||||||
{
|
|
||||||
if (!PQconsumeInput(conn))
|
|
||||||
{
|
|
||||||
/* connection trouble */
|
|
||||||
failed = true;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res = PQgetResult(conn);
|
/* If we need to re-issue the cancel request, do that. */
|
||||||
if (res == NULL)
|
if (now >= retrycanceltime)
|
||||||
break; /* query is complete */
|
{
|
||||||
|
/* We ignore failure to issue the repeated request. */
|
||||||
|
(void) libpqsrv_cancel(conn, endtime);
|
||||||
|
|
||||||
PQclear(last_res);
|
/* Recompute "now" in case that took measurable time. */
|
||||||
last_res = res;
|
now = GetCurrentTimestamp();
|
||||||
|
|
||||||
|
/* Adjust re-cancel timeout in increasing steps. */
|
||||||
|
retrycanceltime = TimestampTzPlusMilliseconds(now,
|
||||||
|
canceldelta);
|
||||||
|
canceldelta += canceldelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If timeout has expired, give up, else get sleep time. */
|
||||||
|
cur_timeout = TimestampDifferenceMilliseconds(now,
|
||||||
|
Min(endtime,
|
||||||
|
retrycanceltime));
|
||||||
|
if (cur_timeout <= 0)
|
||||||
|
{
|
||||||
|
*timed_out = true;
|
||||||
|
failed = true;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* first time, allocate or get the custom wait event */
|
||||||
|
if (pgfdw_we_cleanup_result == 0)
|
||||||
|
pgfdw_we_cleanup_result = WaitEventExtensionNew("PostgresFdwCleanupResult");
|
||||||
|
|
||||||
|
/* Sleep until there's something to do */
|
||||||
|
wc = WaitLatchOrSocket(MyLatch,
|
||||||
|
WL_LATCH_SET | WL_SOCKET_READABLE |
|
||||||
|
WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,
|
||||||
|
PQsocket(conn),
|
||||||
|
cur_timeout, pgfdw_we_cleanup_result);
|
||||||
|
ResetLatch(MyLatch);
|
||||||
|
|
||||||
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
|
/* Data available in socket? */
|
||||||
|
if (wc & WL_SOCKET_READABLE)
|
||||||
|
{
|
||||||
|
if (!PQconsumeInput(conn))
|
||||||
|
{
|
||||||
|
/* connection trouble */
|
||||||
|
failed = true;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res = PQgetResult(conn);
|
||||||
|
if (res == NULL)
|
||||||
|
break; /* query is complete */
|
||||||
|
|
||||||
|
PQclear(last_res);
|
||||||
|
last_res = res;
|
||||||
|
}
|
||||||
exit:
|
exit:
|
||||||
if (failed)
|
if (failed)
|
||||||
PQclear(last_res);
|
PQclear(last_res);
|
||||||
|
@ -3605,31 +3605,31 @@ get_remote_estimate(const char *sql, PGconn *conn,
|
|||||||
Cost *startup_cost, Cost *total_cost)
|
Cost *startup_cost, Cost *total_cost)
|
||||||
{
|
{
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
char *line;
|
char *line;
|
||||||
char *p;
|
char *p;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute EXPLAIN remotely.
|
* Execute EXPLAIN remotely.
|
||||||
*/
|
*/
|
||||||
res = pgfdw_exec_query(conn, sql, NULL);
|
res = pgfdw_exec_query(conn, sql, NULL);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, sql);
|
pgfdw_report_error(ERROR, res, conn, sql);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Extract cost numbers for topmost plan node. Note we search for a
|
* Extract cost numbers for topmost plan node. Note we search for a left
|
||||||
* left paren from the end of the line to avoid being confused by
|
* paren from the end of the line to avoid being confused by other uses of
|
||||||
* other uses of parentheses.
|
* parentheses.
|
||||||
*/
|
*/
|
||||||
line = PQgetvalue(res, 0, 0);
|
line = PQgetvalue(res, 0, 0);
|
||||||
p = strrchr(line, '(');
|
p = strrchr(line, '(');
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
|
elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
|
||||||
n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
|
n = sscanf(p, "(cost=%lf..%lf rows=%lf width=%d)",
|
||||||
startup_cost, total_cost, rows, width);
|
startup_cost, total_cost, rows, width);
|
||||||
if (n != 4)
|
if (n != 4)
|
||||||
elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
|
elog(ERROR, "could not interpret EXPLAIN output: \"%s\"", line);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3812,63 +3812,63 @@ fetch_more_data(ForeignScanState *node)
|
|||||||
MemoryContextReset(fsstate->batch_cxt);
|
MemoryContextReset(fsstate->batch_cxt);
|
||||||
oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
|
oldcontext = MemoryContextSwitchTo(fsstate->batch_cxt);
|
||||||
|
|
||||||
if (fsstate->async_capable)
|
if (fsstate->async_capable)
|
||||||
{
|
{
|
||||||
Assert(fsstate->conn_state->pendingAreq);
|
Assert(fsstate->conn_state->pendingAreq);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The query was already sent by an earlier call to
|
* The query was already sent by an earlier call to
|
||||||
* fetch_more_data_begin. So now we just fetch the result.
|
* fetch_more_data_begin. So now we just fetch the result.
|
||||||
*/
|
*/
|
||||||
res = pgfdw_get_result(conn);
|
res = pgfdw_get_result(conn);
|
||||||
/* On error, report the original query, not the FETCH. */
|
/* On error, report the original query, not the FETCH. */
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, fsstate->query);
|
pgfdw_report_error(ERROR, res, conn, fsstate->query);
|
||||||
|
|
||||||
/* Reset per-connection state */
|
/* Reset per-connection state */
|
||||||
fsstate->conn_state->pendingAreq = NULL;
|
fsstate->conn_state->pendingAreq = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char sql[64];
|
char sql[64];
|
||||||
|
|
||||||
/* This is a regular synchronous fetch. */
|
/* This is a regular synchronous fetch. */
|
||||||
snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
|
snprintf(sql, sizeof(sql), "FETCH %d FROM c%u",
|
||||||
fsstate->fetch_size, fsstate->cursor_number);
|
fsstate->fetch_size, fsstate->cursor_number);
|
||||||
|
|
||||||
res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
|
res = pgfdw_exec_query(conn, sql, fsstate->conn_state);
|
||||||
/* On error, report the original query, not the FETCH. */
|
/* On error, report the original query, not the FETCH. */
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, fsstate->query);
|
pgfdw_report_error(ERROR, res, conn, fsstate->query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Convert the data into HeapTuples */
|
/* Convert the data into HeapTuples */
|
||||||
numrows = PQntuples(res);
|
numrows = PQntuples(res);
|
||||||
fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
|
fsstate->tuples = (HeapTuple *) palloc0(numrows * sizeof(HeapTuple));
|
||||||
fsstate->num_tuples = numrows;
|
fsstate->num_tuples = numrows;
|
||||||
fsstate->next_tuple = 0;
|
fsstate->next_tuple = 0;
|
||||||
|
|
||||||
for (i = 0; i < numrows; i++)
|
for (i = 0; i < numrows; i++)
|
||||||
{
|
{
|
||||||
Assert(IsA(node->ss.ps.plan, ForeignScan));
|
Assert(IsA(node->ss.ps.plan, ForeignScan));
|
||||||
|
|
||||||
fsstate->tuples[i] =
|
fsstate->tuples[i] =
|
||||||
make_tuple_from_result_row(res, i,
|
make_tuple_from_result_row(res, i,
|
||||||
fsstate->rel,
|
fsstate->rel,
|
||||||
fsstate->attinmeta,
|
fsstate->attinmeta,
|
||||||
fsstate->retrieved_attrs,
|
fsstate->retrieved_attrs,
|
||||||
node,
|
node,
|
||||||
fsstate->temp_cxt);
|
fsstate->temp_cxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update fetch_ct_2 */
|
/* Update fetch_ct_2 */
|
||||||
if (fsstate->fetch_ct_2 < 2)
|
if (fsstate->fetch_ct_2 < 2)
|
||||||
fsstate->fetch_ct_2++;
|
fsstate->fetch_ct_2++;
|
||||||
|
|
||||||
/* Must be EOF if we didn't get as many tuples as we asked for. */
|
/* Must be EOF if we didn't get as many tuples as we asked for. */
|
||||||
fsstate->eof_reached = (numrows < fsstate->fetch_size);
|
fsstate->eof_reached = (numrows < fsstate->fetch_size);
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
}
|
}
|
||||||
@ -4322,20 +4322,20 @@ static void
|
|||||||
store_returning_result(PgFdwModifyState *fmstate,
|
store_returning_result(PgFdwModifyState *fmstate,
|
||||||
TupleTableSlot *slot, PGresult *res)
|
TupleTableSlot *slot, PGresult *res)
|
||||||
{
|
{
|
||||||
HeapTuple newtup;
|
HeapTuple newtup;
|
||||||
|
|
||||||
newtup = make_tuple_from_result_row(res, 0,
|
newtup = make_tuple_from_result_row(res, 0,
|
||||||
fmstate->rel,
|
fmstate->rel,
|
||||||
fmstate->attinmeta,
|
fmstate->attinmeta,
|
||||||
fmstate->retrieved_attrs,
|
fmstate->retrieved_attrs,
|
||||||
NULL,
|
NULL,
|
||||||
fmstate->temp_cxt);
|
fmstate->temp_cxt);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The returning slot will not necessarily be suitable to store
|
* The returning slot will not necessarily be suitable to store heaptuples
|
||||||
* heaptuples directly, so allow for conversion.
|
* directly, so allow for conversion.
|
||||||
*/
|
*/
|
||||||
ExecForceStoreHeapTuple(newtup, slot, true);
|
ExecForceStoreHeapTuple(newtup, slot, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4921,14 +4921,14 @@ postgresAnalyzeForeignTable(Relation relation,
|
|||||||
initStringInfo(&sql);
|
initStringInfo(&sql);
|
||||||
deparseAnalyzeSizeSql(&sql, relation);
|
deparseAnalyzeSizeSql(&sql, relation);
|
||||||
|
|
||||||
res = pgfdw_exec_query(conn, sql.data, NULL);
|
res = pgfdw_exec_query(conn, sql.data, NULL);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, sql.data);
|
pgfdw_report_error(ERROR, res, conn, sql.data);
|
||||||
|
|
||||||
if (PQntuples(res) != 1 || PQnfields(res) != 1)
|
if (PQntuples(res) != 1 || PQnfields(res) != 1)
|
||||||
elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
|
elog(ERROR, "unexpected result from deparseAnalyzeSizeSql query");
|
||||||
*totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
|
*totalpages = strtoul(PQgetvalue(res, 0, 0), NULL, 10);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
ReleaseConnection(conn);
|
ReleaseConnection(conn);
|
||||||
|
|
||||||
@ -4970,15 +4970,15 @@ postgresGetAnalyzeInfoForForeignTable(Relation relation, bool *can_tablesample)
|
|||||||
initStringInfo(&sql);
|
initStringInfo(&sql);
|
||||||
deparseAnalyzeInfoSql(&sql, relation);
|
deparseAnalyzeInfoSql(&sql, relation);
|
||||||
|
|
||||||
res = pgfdw_exec_query(conn, sql.data, NULL);
|
res = pgfdw_exec_query(conn, sql.data, NULL);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, sql.data);
|
pgfdw_report_error(ERROR, res, conn, sql.data);
|
||||||
|
|
||||||
if (PQntuples(res) != 1 || PQnfields(res) != 2)
|
if (PQntuples(res) != 1 || PQnfields(res) != 2)
|
||||||
elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
|
elog(ERROR, "unexpected result from deparseAnalyzeInfoSql query");
|
||||||
reltuples = strtod(PQgetvalue(res, 0, 0), NULL);
|
reltuples = strtod(PQgetvalue(res, 0, 0), NULL);
|
||||||
relkind = *(PQgetvalue(res, 0, 1));
|
relkind = *(PQgetvalue(res, 0, 1));
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
ReleaseConnection(conn);
|
ReleaseConnection(conn);
|
||||||
|
|
||||||
@ -5200,76 +5200,76 @@ postgresAcquireSampleRowsFunc(Relation relation, int elevel,
|
|||||||
|
|
||||||
deparseAnalyzeSql(&sql, relation, method, sample_frac, &astate.retrieved_attrs);
|
deparseAnalyzeSql(&sql, relation, method, sample_frac, &astate.retrieved_attrs);
|
||||||
|
|
||||||
res = pgfdw_exec_query(conn, sql.data, NULL);
|
res = pgfdw_exec_query(conn, sql.data, NULL);
|
||||||
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
if (PQresultStatus(res) != PGRES_COMMAND_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, sql.data);
|
pgfdw_report_error(ERROR, res, conn, sql.data);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the fetch size. The default is arbitrary, but shouldn't be
|
||||||
|
* enormous.
|
||||||
|
*/
|
||||||
|
fetch_size = 100;
|
||||||
|
foreach(lc, server->options)
|
||||||
|
{
|
||||||
|
DefElem *def = (DefElem *) lfirst(lc);
|
||||||
|
|
||||||
|
if (strcmp(def->defname, "fetch_size") == 0)
|
||||||
|
{
|
||||||
|
(void) parse_int(defGetString(def), &fetch_size, 0, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(lc, table->options)
|
||||||
|
{
|
||||||
|
DefElem *def = (DefElem *) lfirst(lc);
|
||||||
|
|
||||||
|
if (strcmp(def->defname, "fetch_size") == 0)
|
||||||
|
{
|
||||||
|
(void) parse_int(defGetString(def), &fetch_size, 0, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Construct command to fetch rows from remote. */
|
||||||
|
snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
|
||||||
|
fetch_size, cursor_number);
|
||||||
|
|
||||||
|
/* Retrieve and process rows a batch at a time. */
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int numrows;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Allow users to cancel long query */
|
||||||
|
CHECK_FOR_INTERRUPTS();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine the fetch size. The default is arbitrary, but shouldn't
|
* XXX possible future improvement: if rowstoskip is large, we could
|
||||||
* be enormous.
|
* issue a MOVE rather than physically fetching the rows, then just
|
||||||
|
* adjust rowstoskip and samplerows appropriately.
|
||||||
*/
|
*/
|
||||||
fetch_size = 100;
|
|
||||||
foreach(lc, server->options)
|
|
||||||
{
|
|
||||||
DefElem *def = (DefElem *) lfirst(lc);
|
|
||||||
|
|
||||||
if (strcmp(def->defname, "fetch_size") == 0)
|
/* Fetch some rows */
|
||||||
{
|
res = pgfdw_exec_query(conn, fetch_sql, NULL);
|
||||||
(void) parse_int(defGetString(def), &fetch_size, 0, NULL);
|
/* On error, report the original query, not the FETCH. */
|
||||||
break;
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
}
|
pgfdw_report_error(ERROR, res, conn, sql.data);
|
||||||
}
|
|
||||||
foreach(lc, table->options)
|
|
||||||
{
|
|
||||||
DefElem *def = (DefElem *) lfirst(lc);
|
|
||||||
|
|
||||||
if (strcmp(def->defname, "fetch_size") == 0)
|
/* Process whatever we got. */
|
||||||
{
|
numrows = PQntuples(res);
|
||||||
(void) parse_int(defGetString(def), &fetch_size, 0, NULL);
|
for (i = 0; i < numrows; i++)
|
||||||
break;
|
analyze_row_processor(res, i, &astate);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Construct command to fetch rows from remote. */
|
PQclear(res);
|
||||||
snprintf(fetch_sql, sizeof(fetch_sql), "FETCH %d FROM c%u",
|
|
||||||
fetch_size, cursor_number);
|
|
||||||
|
|
||||||
/* Retrieve and process rows a batch at a time. */
|
/* Must be EOF if we didn't get all the rows requested. */
|
||||||
for (;;)
|
if (numrows < fetch_size)
|
||||||
{
|
break;
|
||||||
int numrows;
|
}
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Allow users to cancel long query */
|
/* Close the cursor, just to be tidy. */
|
||||||
CHECK_FOR_INTERRUPTS();
|
close_cursor(conn, cursor_number, NULL);
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX possible future improvement: if rowstoskip is large, we
|
|
||||||
* could issue a MOVE rather than physically fetching the rows,
|
|
||||||
* then just adjust rowstoskip and samplerows appropriately.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Fetch some rows */
|
|
||||||
res = pgfdw_exec_query(conn, fetch_sql, NULL);
|
|
||||||
/* On error, report the original query, not the FETCH. */
|
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
|
||||||
pgfdw_report_error(ERROR, res, conn, sql.data);
|
|
||||||
|
|
||||||
/* Process whatever we got. */
|
|
||||||
numrows = PQntuples(res);
|
|
||||||
for (i = 0; i < numrows; i++)
|
|
||||||
analyze_row_processor(res, i, &astate);
|
|
||||||
|
|
||||||
PQclear(res);
|
|
||||||
|
|
||||||
/* Must be EOF if we didn't get all the rows requested. */
|
|
||||||
if (numrows < fetch_size)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the cursor, just to be tidy. */
|
|
||||||
close_cursor(conn, cursor_number, NULL);
|
|
||||||
|
|
||||||
ReleaseConnection(conn);
|
ReleaseConnection(conn);
|
||||||
|
|
||||||
@ -5420,234 +5420,231 @@ postgresImportForeignSchema(ImportForeignSchemaStmt *stmt, Oid serverOid)
|
|||||||
/* Create workspace for strings */
|
/* Create workspace for strings */
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
|
|
||||||
/* Check that the schema really exists */
|
/* Check that the schema really exists */
|
||||||
appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
|
appendStringInfoString(&buf, "SELECT 1 FROM pg_catalog.pg_namespace WHERE nspname = ");
|
||||||
deparseStringLiteral(&buf, stmt->remote_schema);
|
deparseStringLiteral(&buf, stmt->remote_schema);
|
||||||
|
|
||||||
res = pgfdw_exec_query(conn, buf.data, NULL);
|
res = pgfdw_exec_query(conn, buf.data, NULL);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, buf.data);
|
pgfdw_report_error(ERROR, res, conn, buf.data);
|
||||||
|
|
||||||
if (PQntuples(res) != 1)
|
if (PQntuples(res) != 1)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_FDW_SCHEMA_NOT_FOUND),
|
(errcode(ERRCODE_FDW_SCHEMA_NOT_FOUND),
|
||||||
errmsg("schema \"%s\" is not present on foreign server \"%s\"",
|
errmsg("schema \"%s\" is not present on foreign server \"%s\"",
|
||||||
stmt->remote_schema, server->servername)));
|
stmt->remote_schema, server->servername)));
|
||||||
|
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
resetStringInfo(&buf);
|
resetStringInfo(&buf);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetch all table data from this schema, possibly restricted by
|
* Fetch all table data from this schema, possibly restricted by EXCEPT or
|
||||||
* EXCEPT or LIMIT TO. (We don't actually need to pay any attention
|
* LIMIT TO. (We don't actually need to pay any attention to EXCEPT/LIMIT
|
||||||
* to EXCEPT/LIMIT TO here, because the core code will filter the
|
* TO here, because the core code will filter the statements we return
|
||||||
* statements we return according to those lists anyway. But it
|
* according to those lists anyway. But it should save a few cycles to
|
||||||
* should save a few cycles to not process excluded tables in the
|
* not process excluded tables in the first place.)
|
||||||
* first place.)
|
*
|
||||||
*
|
* Import table data for partitions only when they are explicitly
|
||||||
* Import table data for partitions only when they are explicitly
|
* specified in LIMIT TO clause. Otherwise ignore them and only include
|
||||||
* specified in LIMIT TO clause. Otherwise ignore them and only
|
* the definitions of the root partitioned tables to allow access to the
|
||||||
* include the definitions of the root partitioned tables to allow
|
* complete remote data set locally in the schema imported.
|
||||||
* access to the complete remote data set locally in the schema
|
*
|
||||||
* imported.
|
* Note: because we run the connection with search_path restricted to
|
||||||
*
|
* pg_catalog, the format_type() and pg_get_expr() outputs will always
|
||||||
* Note: because we run the connection with search_path restricted to
|
* include a schema name for types/functions in other schemas, which is
|
||||||
* pg_catalog, the format_type() and pg_get_expr() outputs will always
|
* what we want.
|
||||||
* include a schema name for types/functions in other schemas, which
|
*/
|
||||||
* is what we want.
|
appendStringInfoString(&buf,
|
||||||
*/
|
"SELECT relname, "
|
||||||
|
" attname, "
|
||||||
|
" format_type(atttypid, atttypmod), "
|
||||||
|
" attnotnull, "
|
||||||
|
" pg_get_expr(adbin, adrelid), ");
|
||||||
|
|
||||||
|
/* Generated columns are supported since Postgres 12 */
|
||||||
|
if (PQserverVersion(conn) >= 120000)
|
||||||
appendStringInfoString(&buf,
|
appendStringInfoString(&buf,
|
||||||
"SELECT relname, "
|
" attgenerated, ");
|
||||||
" attname, "
|
else
|
||||||
" format_type(atttypid, atttypmod), "
|
|
||||||
" attnotnull, "
|
|
||||||
" pg_get_expr(adbin, adrelid), ");
|
|
||||||
|
|
||||||
/* Generated columns are supported since Postgres 12 */
|
|
||||||
if (PQserverVersion(conn) >= 120000)
|
|
||||||
appendStringInfoString(&buf,
|
|
||||||
" attgenerated, ");
|
|
||||||
else
|
|
||||||
appendStringInfoString(&buf,
|
|
||||||
" NULL, ");
|
|
||||||
|
|
||||||
if (import_collate)
|
|
||||||
appendStringInfoString(&buf,
|
|
||||||
" collname, "
|
|
||||||
" collnsp.nspname ");
|
|
||||||
else
|
|
||||||
appendStringInfoString(&buf,
|
|
||||||
" NULL, NULL ");
|
|
||||||
|
|
||||||
appendStringInfoString(&buf,
|
appendStringInfoString(&buf,
|
||||||
"FROM pg_class c "
|
" NULL, ");
|
||||||
" JOIN pg_namespace n ON "
|
|
||||||
" relnamespace = n.oid "
|
|
||||||
" LEFT JOIN pg_attribute a ON "
|
|
||||||
" attrelid = c.oid AND attnum > 0 "
|
|
||||||
" AND NOT attisdropped "
|
|
||||||
" LEFT JOIN pg_attrdef ad ON "
|
|
||||||
" adrelid = c.oid AND adnum = attnum ");
|
|
||||||
|
|
||||||
if (import_collate)
|
|
||||||
appendStringInfoString(&buf,
|
|
||||||
" LEFT JOIN pg_collation coll ON "
|
|
||||||
" coll.oid = attcollation "
|
|
||||||
" LEFT JOIN pg_namespace collnsp ON "
|
|
||||||
" collnsp.oid = collnamespace ");
|
|
||||||
|
|
||||||
|
if (import_collate)
|
||||||
appendStringInfoString(&buf,
|
appendStringInfoString(&buf,
|
||||||
"WHERE c.relkind IN ("
|
" collname, "
|
||||||
CppAsString2(RELKIND_RELATION) ","
|
" collnsp.nspname ");
|
||||||
CppAsString2(RELKIND_VIEW) ","
|
else
|
||||||
CppAsString2(RELKIND_FOREIGN_TABLE) ","
|
appendStringInfoString(&buf,
|
||||||
CppAsString2(RELKIND_MATVIEW) ","
|
" NULL, NULL ");
|
||||||
CppAsString2(RELKIND_PARTITIONED_TABLE) ") "
|
|
||||||
" AND n.nspname = ");
|
|
||||||
deparseStringLiteral(&buf, stmt->remote_schema);
|
|
||||||
|
|
||||||
/* Partitions are supported since Postgres 10 */
|
appendStringInfoString(&buf,
|
||||||
if (PQserverVersion(conn) >= 100000 &&
|
"FROM pg_class c "
|
||||||
stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
|
" JOIN pg_namespace n ON "
|
||||||
appendStringInfoString(&buf, " AND NOT c.relispartition ");
|
" relnamespace = n.oid "
|
||||||
|
" LEFT JOIN pg_attribute a ON "
|
||||||
|
" attrelid = c.oid AND attnum > 0 "
|
||||||
|
" AND NOT attisdropped "
|
||||||
|
" LEFT JOIN pg_attrdef ad ON "
|
||||||
|
" adrelid = c.oid AND adnum = attnum ");
|
||||||
|
|
||||||
/* Apply restrictions for LIMIT TO and EXCEPT */
|
if (import_collate)
|
||||||
if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
|
appendStringInfoString(&buf,
|
||||||
stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
|
" LEFT JOIN pg_collation coll ON "
|
||||||
|
" coll.oid = attcollation "
|
||||||
|
" LEFT JOIN pg_namespace collnsp ON "
|
||||||
|
" collnsp.oid = collnamespace ");
|
||||||
|
|
||||||
|
appendStringInfoString(&buf,
|
||||||
|
"WHERE c.relkind IN ("
|
||||||
|
CppAsString2(RELKIND_RELATION) ","
|
||||||
|
CppAsString2(RELKIND_VIEW) ","
|
||||||
|
CppAsString2(RELKIND_FOREIGN_TABLE) ","
|
||||||
|
CppAsString2(RELKIND_MATVIEW) ","
|
||||||
|
CppAsString2(RELKIND_PARTITIONED_TABLE) ") "
|
||||||
|
" AND n.nspname = ");
|
||||||
|
deparseStringLiteral(&buf, stmt->remote_schema);
|
||||||
|
|
||||||
|
/* Partitions are supported since Postgres 10 */
|
||||||
|
if (PQserverVersion(conn) >= 100000 &&
|
||||||
|
stmt->list_type != FDW_IMPORT_SCHEMA_LIMIT_TO)
|
||||||
|
appendStringInfoString(&buf, " AND NOT c.relispartition ");
|
||||||
|
|
||||||
|
/* Apply restrictions for LIMIT TO and EXCEPT */
|
||||||
|
if (stmt->list_type == FDW_IMPORT_SCHEMA_LIMIT_TO ||
|
||||||
|
stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
|
||||||
|
{
|
||||||
|
bool first_item = true;
|
||||||
|
|
||||||
|
appendStringInfoString(&buf, " AND c.relname ");
|
||||||
|
if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
|
||||||
|
appendStringInfoString(&buf, "NOT ");
|
||||||
|
appendStringInfoString(&buf, "IN (");
|
||||||
|
|
||||||
|
/* Append list of table names within IN clause */
|
||||||
|
foreach(lc, stmt->table_list)
|
||||||
{
|
{
|
||||||
bool first_item = true;
|
RangeVar *rv = (RangeVar *) lfirst(lc);
|
||||||
|
|
||||||
appendStringInfoString(&buf, " AND c.relname ");
|
if (first_item)
|
||||||
if (stmt->list_type == FDW_IMPORT_SCHEMA_EXCEPT)
|
first_item = false;
|
||||||
appendStringInfoString(&buf, "NOT ");
|
else
|
||||||
appendStringInfoString(&buf, "IN (");
|
appendStringInfoString(&buf, ", ");
|
||||||
|
deparseStringLiteral(&buf, rv->relname);
|
||||||
/* Append list of table names within IN clause */
|
|
||||||
foreach(lc, stmt->table_list)
|
|
||||||
{
|
|
||||||
RangeVar *rv = (RangeVar *) lfirst(lc);
|
|
||||||
|
|
||||||
if (first_item)
|
|
||||||
first_item = false;
|
|
||||||
else
|
|
||||||
appendStringInfoString(&buf, ", ");
|
|
||||||
deparseStringLiteral(&buf, rv->relname);
|
|
||||||
}
|
|
||||||
appendStringInfoChar(&buf, ')');
|
|
||||||
}
|
}
|
||||||
|
appendStringInfoChar(&buf, ')');
|
||||||
|
}
|
||||||
|
|
||||||
/* Append ORDER BY at the end of query to ensure output ordering */
|
/* Append ORDER BY at the end of query to ensure output ordering */
|
||||||
appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
|
appendStringInfoString(&buf, " ORDER BY c.relname, a.attnum");
|
||||||
|
|
||||||
/* Fetch the data */
|
/* Fetch the data */
|
||||||
res = pgfdw_exec_query(conn, buf.data, NULL);
|
res = pgfdw_exec_query(conn, buf.data, NULL);
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
pgfdw_report_error(ERROR, res, conn, buf.data);
|
pgfdw_report_error(ERROR, res, conn, buf.data);
|
||||||
|
|
||||||
/* Process results */
|
/* Process results */
|
||||||
numrows = PQntuples(res);
|
numrows = PQntuples(res);
|
||||||
/* note: incrementation of i happens in inner loop's while() test */
|
/* note: incrementation of i happens in inner loop's while() test */
|
||||||
for (i = 0; i < numrows;)
|
for (i = 0; i < numrows;)
|
||||||
|
{
|
||||||
|
char *tablename = PQgetvalue(res, i, 0);
|
||||||
|
bool first_item = true;
|
||||||
|
|
||||||
|
resetStringInfo(&buf);
|
||||||
|
appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
|
||||||
|
quote_identifier(tablename));
|
||||||
|
|
||||||
|
/* Scan all rows for this table */
|
||||||
|
do
|
||||||
{
|
{
|
||||||
char *tablename = PQgetvalue(res, i, 0);
|
char *attname;
|
||||||
bool first_item = true;
|
char *typename;
|
||||||
|
char *attnotnull;
|
||||||
|
char *attgenerated;
|
||||||
|
char *attdefault;
|
||||||
|
char *collname;
|
||||||
|
char *collnamespace;
|
||||||
|
|
||||||
resetStringInfo(&buf);
|
/* If table has no columns, we'll see nulls here */
|
||||||
appendStringInfo(&buf, "CREATE FOREIGN TABLE %s (\n",
|
if (PQgetisnull(res, i, 1))
|
||||||
quote_identifier(tablename));
|
continue;
|
||||||
|
|
||||||
/* Scan all rows for this table */
|
attname = PQgetvalue(res, i, 1);
|
||||||
do
|
typename = PQgetvalue(res, i, 2);
|
||||||
{
|
attnotnull = PQgetvalue(res, i, 3);
|
||||||
char *attname;
|
attdefault = PQgetisnull(res, i, 4) ? NULL :
|
||||||
char *typename;
|
PQgetvalue(res, i, 4);
|
||||||
char *attnotnull;
|
attgenerated = PQgetisnull(res, i, 5) ? NULL :
|
||||||
char *attgenerated;
|
PQgetvalue(res, i, 5);
|
||||||
char *attdefault;
|
collname = PQgetisnull(res, i, 6) ? NULL :
|
||||||
char *collname;
|
PQgetvalue(res, i, 6);
|
||||||
char *collnamespace;
|
collnamespace = PQgetisnull(res, i, 7) ? NULL :
|
||||||
|
PQgetvalue(res, i, 7);
|
||||||
|
|
||||||
/* If table has no columns, we'll see nulls here */
|
if (first_item)
|
||||||
if (PQgetisnull(res, i, 1))
|
first_item = false;
|
||||||
continue;
|
else
|
||||||
|
appendStringInfoString(&buf, ",\n");
|
||||||
|
|
||||||
attname = PQgetvalue(res, i, 1);
|
/* Print column name and type */
|
||||||
typename = PQgetvalue(res, i, 2);
|
appendStringInfo(&buf, " %s %s",
|
||||||
attnotnull = PQgetvalue(res, i, 3);
|
quote_identifier(attname),
|
||||||
attdefault = PQgetisnull(res, i, 4) ? NULL :
|
typename);
|
||||||
PQgetvalue(res, i, 4);
|
|
||||||
attgenerated = PQgetisnull(res, i, 5) ? NULL :
|
|
||||||
PQgetvalue(res, i, 5);
|
|
||||||
collname = PQgetisnull(res, i, 6) ? NULL :
|
|
||||||
PQgetvalue(res, i, 6);
|
|
||||||
collnamespace = PQgetisnull(res, i, 7) ? NULL :
|
|
||||||
PQgetvalue(res, i, 7);
|
|
||||||
|
|
||||||
if (first_item)
|
|
||||||
first_item = false;
|
|
||||||
else
|
|
||||||
appendStringInfoString(&buf, ",\n");
|
|
||||||
|
|
||||||
/* Print column name and type */
|
|
||||||
appendStringInfo(&buf, " %s %s",
|
|
||||||
quote_identifier(attname),
|
|
||||||
typename);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add column_name option so that renaming the foreign table's
|
|
||||||
* column doesn't break the association to the underlying
|
|
||||||
* column.
|
|
||||||
*/
|
|
||||||
appendStringInfoString(&buf, " OPTIONS (column_name ");
|
|
||||||
deparseStringLiteral(&buf, attname);
|
|
||||||
appendStringInfoChar(&buf, ')');
|
|
||||||
|
|
||||||
/* Add COLLATE if needed */
|
|
||||||
if (import_collate && collname != NULL && collnamespace != NULL)
|
|
||||||
appendStringInfo(&buf, " COLLATE %s.%s",
|
|
||||||
quote_identifier(collnamespace),
|
|
||||||
quote_identifier(collname));
|
|
||||||
|
|
||||||
/* Add DEFAULT if needed */
|
|
||||||
if (import_default && attdefault != NULL &&
|
|
||||||
(!attgenerated || !attgenerated[0]))
|
|
||||||
appendStringInfo(&buf, " DEFAULT %s", attdefault);
|
|
||||||
|
|
||||||
/* Add GENERATED if needed */
|
|
||||||
if (import_generated && attgenerated != NULL &&
|
|
||||||
attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
|
|
||||||
{
|
|
||||||
Assert(attdefault != NULL);
|
|
||||||
appendStringInfo(&buf,
|
|
||||||
" GENERATED ALWAYS AS (%s) STORED",
|
|
||||||
attdefault);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add NOT NULL if needed */
|
|
||||||
if (import_not_null && attnotnull[0] == 't')
|
|
||||||
appendStringInfoString(&buf, " NOT NULL");
|
|
||||||
}
|
|
||||||
while (++i < numrows &&
|
|
||||||
strcmp(PQgetvalue(res, i, 0), tablename) == 0);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add server name and table-level options. We specify remote
|
* Add column_name option so that renaming the foreign table's
|
||||||
* schema and table name as options (the latter to ensure that
|
* column doesn't break the association to the underlying column.
|
||||||
* renaming the foreign table doesn't break the association).
|
|
||||||
*/
|
*/
|
||||||
appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
|
appendStringInfoString(&buf, " OPTIONS (column_name ");
|
||||||
quote_identifier(server->servername));
|
deparseStringLiteral(&buf, attname);
|
||||||
|
appendStringInfoChar(&buf, ')');
|
||||||
|
|
||||||
appendStringInfoString(&buf, "schema_name ");
|
/* Add COLLATE if needed */
|
||||||
deparseStringLiteral(&buf, stmt->remote_schema);
|
if (import_collate && collname != NULL && collnamespace != NULL)
|
||||||
appendStringInfoString(&buf, ", table_name ");
|
appendStringInfo(&buf, " COLLATE %s.%s",
|
||||||
deparseStringLiteral(&buf, tablename);
|
quote_identifier(collnamespace),
|
||||||
|
quote_identifier(collname));
|
||||||
|
|
||||||
appendStringInfoString(&buf, ");");
|
/* Add DEFAULT if needed */
|
||||||
|
if (import_default && attdefault != NULL &&
|
||||||
|
(!attgenerated || !attgenerated[0]))
|
||||||
|
appendStringInfo(&buf, " DEFAULT %s", attdefault);
|
||||||
|
|
||||||
commands = lappend(commands, pstrdup(buf.data));
|
/* Add GENERATED if needed */
|
||||||
|
if (import_generated && attgenerated != NULL &&
|
||||||
|
attgenerated[0] == ATTRIBUTE_GENERATED_STORED)
|
||||||
|
{
|
||||||
|
Assert(attdefault != NULL);
|
||||||
|
appendStringInfo(&buf,
|
||||||
|
" GENERATED ALWAYS AS (%s) STORED",
|
||||||
|
attdefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add NOT NULL if needed */
|
||||||
|
if (import_not_null && attnotnull[0] == 't')
|
||||||
|
appendStringInfoString(&buf, " NOT NULL");
|
||||||
}
|
}
|
||||||
PQclear(res);
|
while (++i < numrows &&
|
||||||
|
strcmp(PQgetvalue(res, i, 0), tablename) == 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add server name and table-level options. We specify remote schema
|
||||||
|
* and table name as options (the latter to ensure that renaming the
|
||||||
|
* foreign table doesn't break the association).
|
||||||
|
*/
|
||||||
|
appendStringInfo(&buf, "\n) SERVER %s\nOPTIONS (",
|
||||||
|
quote_identifier(server->servername));
|
||||||
|
|
||||||
|
appendStringInfoString(&buf, "schema_name ");
|
||||||
|
deparseStringLiteral(&buf, stmt->remote_schema);
|
||||||
|
appendStringInfoString(&buf, ", table_name ");
|
||||||
|
deparseStringLiteral(&buf, tablename);
|
||||||
|
|
||||||
|
appendStringInfoString(&buf, ");");
|
||||||
|
|
||||||
|
commands = lappend(commands, pstrdup(buf.data));
|
||||||
|
}
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
ReleaseConnection(conn);
|
ReleaseConnection(conn);
|
||||||
|
|
||||||
|
@ -281,28 +281,28 @@ libpqsrv_get_result_last(PGconn *conn, uint32 wait_event_info)
|
|||||||
{
|
{
|
||||||
PGresult *lastResult = NULL;
|
PGresult *lastResult = NULL;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
/* Wait for, and collect, the next PGresult. */
|
/* Wait for, and collect, the next PGresult. */
|
||||||
PGresult *result;
|
PGresult *result;
|
||||||
|
|
||||||
result = libpqsrv_get_result(conn, wait_event_info);
|
result = libpqsrv_get_result(conn, wait_event_info);
|
||||||
if (result == NULL)
|
if (result == NULL)
|
||||||
break; /* query is complete, or failure */
|
break; /* query is complete, or failure */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Emulate PQexec()'s behavior of returning the last result when
|
* Emulate PQexec()'s behavior of returning the last result when there
|
||||||
* there are many.
|
* are many.
|
||||||
*/
|
*/
|
||||||
PQclear(lastResult);
|
PQclear(lastResult);
|
||||||
lastResult = result;
|
lastResult = result;
|
||||||
|
|
||||||
if (PQresultStatus(lastResult) == PGRES_COPY_IN ||
|
if (PQresultStatus(lastResult) == PGRES_COPY_IN ||
|
||||||
PQresultStatus(lastResult) == PGRES_COPY_OUT ||
|
PQresultStatus(lastResult) == PGRES_COPY_OUT ||
|
||||||
PQresultStatus(lastResult) == PGRES_COPY_BOTH ||
|
PQresultStatus(lastResult) == PGRES_COPY_BOTH ||
|
||||||
PQstatus(conn) == CONNECTION_BAD)
|
PQstatus(conn) == CONNECTION_BAD)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return lastResult;
|
return lastResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user