| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| |
| #include "pxfbridge.h" |
| #include "pxffilters.h" |
| #include "extension/gp_exttable_fdw/extaccess.h" |
| #include "utils/elog.h" |
| |
| /* define magic module unless run as a part of test cases */ |
| #ifndef UNIT_TESTING |
| PG_MODULE_MAGIC; |
| #endif |
| |
| PG_FUNCTION_INFO_V1(pxfprotocol_export); |
| PG_FUNCTION_INFO_V1(pxfprotocol_import); |
| PG_FUNCTION_INFO_V1(pxfprotocol_validate_urls); |
| |
| /* public function declarations */ |
| Datum pxfprotocol_export(PG_FUNCTION_ARGS); |
| Datum pxfprotocol_import(PG_FUNCTION_ARGS); |
| Datum pxfprotocol_validate_urls(PG_FUNCTION_ARGS); |
| |
| /* helper function declarations */ |
| static gphadoop_context *create_context(PG_FUNCTION_ARGS, bool is_import); |
| static void cleanup_context(gphadoop_context *context); |
| static void check_caller(PG_FUNCTION_ARGS, const char *func_name); |
| |
| /* |
| * Validates external table URL |
| */ |
| Datum |
| pxfprotocol_validate_urls(PG_FUNCTION_ARGS) |
| { |
| /* Must be called via the external table format manager */ |
| if (!CALLED_AS_EXTPROTOCOL_VALIDATOR(fcinfo)) |
| elog(ERROR, "cannot execute pxfprotocol_validate_urls outside protocol manager"); |
| |
| /* There must be only ONE url. */ |
| if (EXTPROTOCOL_VALIDATOR_GET_NUM_URLS(fcinfo) != 1) |
| ereport(ERROR, |
| (errcode(ERRCODE_PROTOCOL_VIOLATION), |
| errmsg("number of URLs must be one"))); |
| |
| char *uri_string = EXTPROTOCOL_VALIDATOR_GET_NTH_URL(fcinfo, 1); |
| |
| elog(DEBUG2, "pxfprotocol_validate_urls: uri %s", uri_string); |
| GPHDUri *uri = parseGPHDUri(uri_string); |
| |
| /* No duplicate options. */ |
| GPHDUri_verify_no_duplicate_options(uri); |
| |
| /* Check for existence of core options if profile wasn't supplied */ |
| if (!GPHDUri_opt_exists(uri, PXF_PROFILE)) |
| { |
| List *coreOptions = list_make2(ACCESSOR, RESOLVER); |
| if (EXTPROTOCOL_VALIDATOR_GET_DIRECTION(fcinfo) != EXT_VALIDATE_WRITE) |
| coreOptions = lcons(FRAGMENTER, coreOptions); |
| |
| GPHDUri_verify_core_options_exist(uri, coreOptions); |
| list_free(coreOptions); |
| } |
| |
| freeGPHDUri(uri); |
| PG_RETURN_VOID(); |
| } |
| |
| /* |
| * Writes to an external table |
| */ |
| Datum |
| pxfprotocol_export(PG_FUNCTION_ARGS) |
| { |
| /* Must be called via the external table format manager */ |
| check_caller(fcinfo, "pxfprotocol_export"); |
| /* retrieve user context required for data write */ |
| gphadoop_context *context = (gphadoop_context *) EXTPROTOCOL_GET_USER_CTX(fcinfo); |
| |
| /* last call -- cleanup */ |
| if (EXTPROTOCOL_IS_LAST_CALL(fcinfo)) |
| { |
| cleanup_context(context); |
| EXTPROTOCOL_SET_USER_CTX(fcinfo, NULL); |
| PG_RETURN_INT32(0); |
| } |
| |
| /* first call -- do any desired init */ |
| if (context == NULL) |
| { |
| context = create_context(fcinfo, false); |
| EXTPROTOCOL_SET_USER_CTX(fcinfo, context); |
| gpbridge_export_start(context); |
| } |
| /* Read data */ |
| int bytes_written = gpbridge_write(context, EXTPROTOCOL_GET_DATABUF(fcinfo), EXTPROTOCOL_GET_DATALEN(fcinfo)); |
| |
| PG_RETURN_INT32(bytes_written); |
| } |
| |
| /* |
| * Reads tuples from an external table |
| */ |
| Datum |
| pxfprotocol_import(PG_FUNCTION_ARGS) |
| { |
| /* Must be called via the external table format manager */ |
| check_caller(fcinfo, "pxfprotocol_import"); |
| /* retrieve user context required for data read */ |
| gphadoop_context *context = (gphadoop_context *) EXTPROTOCOL_GET_USER_CTX(fcinfo); |
| |
| /* last call -- cleanup */ |
| if (EXTPROTOCOL_IS_LAST_CALL(fcinfo)) |
| { |
| cleanup_context(context); |
| EXTPROTOCOL_SET_USER_CTX(fcinfo, NULL); |
| PG_RETURN_INT32(0); |
| } |
| /* first call -- do any desired init */ |
| if (context == NULL) |
| { |
| context = create_context(fcinfo, true); |
| EXTPROTOCOL_SET_USER_CTX(fcinfo, context); |
| gpbridge_import_start(context); |
| } |
| /* sometimes an additional call can be executed even when we completed reading data from the stream */ |
| if (context->completed) |
| { |
| PG_RETURN_INT32(0); |
| } |
| |
| /* Read data */ |
| int bytes_read = gpbridge_read(context, EXTPROTOCOL_GET_DATABUF(fcinfo), EXTPROTOCOL_GET_DATALEN(fcinfo)); |
| |
| PG_RETURN_INT32(bytes_read); |
| } |
| |
| /* |
| * Allocates context and sets values for the segment |
| */ |
| static gphadoop_context * |
| create_context(PG_FUNCTION_ARGS, bool is_import) |
| { |
| /* parse and set uri */ |
| GPHDUri *uri = parseGPHDUri(EXTPROTOCOL_GET_URL(fcinfo)); |
| Relation relation = EXTPROTOCOL_GET_RELATION(fcinfo); |
| List *filter_quals = NULL; |
| ProjectionInfo *proj_info = NULL; |
| char *filterstr = NULL; |
| ExternalSelectDescData *desc = EXTPROTOCOL_GET_EXTERNAL_SELECT_DESC(fcinfo); |
| |
| if (desc != NULL) |
| { |
| filter_quals = desc->filter_quals; |
| proj_info = desc->projInfo; |
| |
| if (filter_quals != NULL) |
| { |
| elog(DEBUG1, "create_context: filter_quals is provided"); |
| filterstr = serializePxfFilterQuals(filter_quals); |
| } |
| } |
| |
| /* set context */ |
| gphadoop_context *context = palloc0(sizeof(gphadoop_context)); |
| |
| context->gphd_uri = uri; |
| initStringInfo(&context->uri); |
| context->relation = relation; |
| context->filterstr = filterstr; |
| context->proj_info = proj_info; |
| context->quals = filter_quals; |
| context->completed = false; |
| return context; |
| } |
| |
| /* |
| * De-allocates context and dependent structures. |
| */ |
| static void |
| cleanup_context(gphadoop_context *context) |
| { |
| if (context != NULL) |
| { |
| gpbridge_cleanup(context); |
| pfree(context->uri.data); |
| pfree(context); |
| } |
| } |
| |
| /* |
| * Checks that the caller is External Protocol Manager |
| */ |
| static void |
| check_caller(PG_FUNCTION_ARGS, const char *func_name) |
| { |
| if (!CALLED_AS_EXTPROTOCOL(fcinfo)) |
| ereport(ERROR, |
| (errcode(ERRCODE_INTERNAL_ERROR), |
| errmsg("%s not called by external protocol manager", func_name))); |
| } |