fdw-textarray.patch2

text/plain

Filename: fdw-textarray.patch2
Type: text/plain
Part: 0
Message: Re: textarray option for file FDW
*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
***************
*** 58,67 **** static struct FileFdwOption valid_options[] = {
  	{ "quote",			ForeignTableRelationId },
  	{ "escape",			ForeignTableRelationId },
  	{ "null",			ForeignTableRelationId },
  
  	/* FIXME: implement force_not_null option */
  
! 	/* Centinel */
  	{ NULL,			InvalidOid }
  };
  
--- 58,68 ----
  	{ "quote",			ForeignTableRelationId },
  	{ "escape",			ForeignTableRelationId },
  	{ "null",			ForeignTableRelationId },
+ 	{ "textarray",		ForeignTableRelationId },
  
  	/* FIXME: implement force_not_null option */
  
! 	/* Sentinel */
  	{ NULL,			InvalidOid }
  };
  
***************
*** 134,139 **** file_fdw_validator(PG_FUNCTION_ARGS)
--- 135,141 ----
  	char	   *escape = NULL;
  	char	   *null = NULL;
  	bool		header;
+ 	bool        textarray;
  
  	/* Only superuser can change generic options of the foreign table */
  	if (catalog == ForeignTableRelationId && !superuser())
***************
*** 220,225 **** file_fdw_validator(PG_FUNCTION_ARGS)
--- 222,231 ----
  						 errmsg("null representation cannot use newline or carriage return")));
  			null = strVal(def->arg);
  		}
+ 		else if (strcmp(def->defname, "textarray") == 0)
+ 		{
+ 			textarray = defGetBoolean(def);
+ 		}
  	}
  
  	/* Check options which depend on the file format. */
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
***************
*** 44,49 ****
--- 44,51 ----
  #include "utils/memutils.h"
  #include "utils/snapmgr.h"
  
+ /* initial size for arrays in textarray mode */
+ #define TEXTARRAY_SIZE 64
  
  #define ISOCTAL(c) (((c) >= '0') && ((c) <= '7'))
  #define OCTVALUE(c) ((c) - '0')
***************
*** 117,122 **** typedef struct CopyStateData
--- 119,127 ----
  	bool	   *force_quote_flags;		/* per-column CSV FQ flags */
  	bool	   *force_notnull_flags;	/* per-column CSV FNN flags */
  
+ 	/* param from FDW */
+ 	bool       text_array;      /* scan to a single text array field */
+ 
  	/* these are just for error messages, see CopyFromErrorCallback */
  	const char *cur_relname;	/* table name for error messages */
  	int			cur_lineno;		/* line number for error messages */
***************
*** 970,975 **** BeginCopy(bool is_from,
--- 975,988 ----
  						 errmsg("argument to option \"%s\" must be a list of column names",
  								defel->defname)));
  		}
+ 		else if (strcmp(defel->defname, "textarray") == 0)
+ 		{
+ 			if (cstate->text_array)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_SYNTAX_ERROR),
+ 						 errmsg("conflicting or redundant options")));
+ 			cstate->text_array = defGetBoolean(defel);
+ 		}
  		else
  			ereport(ERROR,
  					(errcode(ERRCODE_SYNTAX_ERROR),
***************
*** 1109,1114 **** BeginCopy(bool is_from,
--- 1122,1157 ----
  				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
  				 errmsg("CSV quote character must not appear in the NULL specification")));
  
+ 	/*
+ 	 * file_fdw can use textarray mode, and as of the time of writing should not
+ 	 * be able to cause any of these next errors. The checks are here more for
+ 	 * the sake of future-proofing, and as protection in case a third party
+ 	 * module uses the COPY API.
+ 	 */
+ 
+ 	/* check textarray  mode */
+ 	if (cstate->text_array && !is_from)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 			  errmsg("textarray only available in read mode")));
+ 
+ 	/* textarray is only allowed in text and CSV modes */
+ 	if (cstate->text_array && cstate->binary)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 			  errmsg("textarray not available in binary mode")));
+ 
+ 	/* 
+ 	 * textarray can't operate with force_not_null - it will not return
+ 	 * a null in any case, although the array it returns might contain nulls,
+ 	 * which it will be up to the user to deal with.
+ 	 */
+ 
+ 	if (cstate->text_array && force_notnull != NIL)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 			  errmsg("force not null not available with textarray")));
+ 
  	if (rel)
  	{
  		Assert(!raw_query);
***************
*** 1201,1206 **** BeginCopy(bool is_from,
--- 1244,1262 ----
  
  	num_phys_attrs = tupDesc->natts;
  
+ 	/* make sure rel has the right shape for textarray */
+ 	if (cstate->text_array)
+ 	{
+ 		if (num_phys_attrs != 1)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("too many  columns for textarray")));
+ 		if (tupDesc->attrs[0]->atttypid != TEXTARRAYOID)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 					 errmsg("target column must be of type text[] for textarray")));				
+ 	}
+ 
  	/* Convert FORCE QUOTE name list to per-column flags, check validity */
  	cstate->force_quote_flags = (bool *) palloc0(num_phys_attrs * sizeof(bool));
  	if (force_quote_all)
***************
*** 2061,2068 **** BeginCopyFrom(Relation rel,
  	num_defaults = 0;
  
  	/* allocate arrays to avoid per-tuple overhead. */
! 	cstate->values = palloc(sizeof(Datum) * tupDesc->natts);
! 	cstate->nulls = palloc(sizeof(bool) * tupDesc->natts);
  
  	/*
  	 * Pick up the required catalog information for each attribute in the
--- 2117,2132 ----
  	num_defaults = 0;
  
  	/* allocate arrays to avoid per-tuple overhead. */
! 	if (!cstate->text_array)
! 	{
! 		cstate->values = (Datum *) palloc(num_phys_attrs * sizeof(Datum));
! 		cstate->nulls = (bool *) palloc(num_phys_attrs * sizeof(bool));
! 	}
! 	else
! 	{
! 		cstate->values = (Datum *) palloc(TEXTARRAY_SIZE * sizeof(Datum));
! 		cstate->nulls = (bool *) palloc(TEXTARRAY_SIZE * sizeof(bool));
! 	}
  
  	/*
  	 * Pick up the required catalog information for each attribute in the
***************
*** 2194,2200 **** BeginCopyFrom(Relation rel,
  	}
  
  	/* create workspace for CopyReadAttributes results */
! 	if (!cstate->binary)
  	{
  		AttrNumber	attr_count = list_length(cstate->attnumlist);
  		int	nfields = cstate->file_has_oids ? (attr_count + 1) : attr_count;
--- 2258,2269 ----
  	}
  
  	/* create workspace for CopyReadAttributes results */
! 	if (cstate->text_array)
! 	{
! 		cstate->max_fields = TEXTARRAY_SIZE;
! 		cstate->raw_fields = (char **) palloc(TEXTARRAY_SIZE * sizeof(char *));
! 	}
! 	else if (!cstate->binary)
  	{
  		AttrNumber	attr_count = list_length(cstate->attnumlist);
  		int	nfields = cstate->file_has_oids ? (attr_count + 1) : attr_count;
***************
*** 2283,2289 **** NextCopyFrom(CopyState cstate)
  				fldct = CopyReadAttributesText(cstate);
  
  			/* check for overflowing fields */
! 			if (nfields > 0 && fldct > nfields)
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  						 errmsg("extra data after last expected column")));
--- 2352,2358 ----
  				fldct = CopyReadAttributesText(cstate);
  
  			/* check for overflowing fields */
! 			if (nfields > 0 && fldct > nfields && !cstate->text_array)
  				ereport(ERROR,
  						(errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
  						 errmsg("extra data after last expected column")));
***************
*** 2319,2324 **** NextCopyFrom(CopyState cstate)
--- 2388,2447 ----
  				}
  			}
  
+ 			if (cstate->text_array)
+ 			{
+ 				int        dims[1];
+ 				int        lbs[1];
+ 				int        fld;
+ 				
+ 				/* Treat an empty line as having no fields */
+ 				if (fldct == 1 && 
+ 					field_strings[0] == NULL && 
+ 					cstate->null_print_len == 0)
+ 				{
+ 					fldct = 0;
+ 				}
+ 
+ 				dims[0] = fldct;
+ 				lbs[0] = 1; /* sql arrays typically start at 1 */
+ 
+ 				/*
+ 				 * Use the values and nulls in cstate as workspace to create the array,
+ 				 * before storing the result back in the samne arrays.
+ 				 */
+ 
+ 				
+ 				for (fld=0; fld < fldct; fld++)
+ 				{
+ 					if (field_strings[fld] == NULL)
+ 					{
+ 						cstate->nulls[fld] = true;
+ 					}
+ 					else
+ 					{
+ 						cstate->nulls[fld] = false;
+ 						cstate->values[fld] = 
+ 							DirectFunctionCall1(textin, 
+ 												PointerGetDatum(field_strings[fld]));
+ 					}
+ 				}
+ 
+ 				cstate->values[0] =  PointerGetDatum(construct_md_array(
+ 														 cstate->values,
+ 														 cstate->nulls,
+ 														 1,
+ 														 dims,
+ 														 lbs,
+ 														 TEXTOID,
+ 														 -1,
+ 														 false,
+ 														 'i'));
+ 				cstate->nulls[0] = false;
+ 				cstate->cur_attname = NULL;
+ 				cstate->cur_attval = NULL;
+ 			}
+ 			else
+ 			{
  			/* Loop to read the user attributes on the line. */
  			foreach(cur, cstate->attnumlist)
  			{
***************
*** 2350,2357 **** NextCopyFrom(CopyState cstate)
  				cstate->cur_attname = NULL;
  				cstate->cur_attval = NULL;
  			}
  
! 			Assert(fieldno == nfields);
  		}
  		else
  		{
--- 2473,2481 ----
  				cstate->cur_attname = NULL;
  				cstate->cur_attval = NULL;
  			}
+ 			}
  
! 			Assert(cstate->text_array || fieldno == nfields);
  		}
  		else
  		{
***************
*** 3011,3016 **** CopyReadAttributesText(CopyState cstate)
--- 3135,3147 ----
  			cstate->max_fields *= 2;
  			cstate->raw_fields = 
  				repalloc(cstate->raw_fields, cstate->max_fields*sizeof(char *));
+ 			if (cstate->text_array)
+ 			{
+ 				cstate->values = repalloc(cstate->values, 
+ 										  cstate->max_fields*sizeof(Datum));
+ 				cstate->nulls = repalloc(cstate->nulls, 
+ 										  cstate->max_fields*sizeof(bool));
+ 			}
  		}
  
  		/* Remember start of field on both input and output sides */
***************
*** 3228,3233 **** CopyReadAttributesCSV(CopyState cstate)
--- 3359,3371 ----
  			cstate->max_fields *= 2;
  			cstate->raw_fields = 
  				repalloc(cstate->raw_fields, cstate->max_fields*sizeof(char *));
+ 			if (cstate->text_array)
+ 			{
+ 				cstate->values = repalloc(cstate->values, 
+ 										  cstate->max_fields*sizeof(Datum));
+ 				cstate->nulls = repalloc(cstate->nulls, 
+ 										  cstate->max_fields*sizeof(bool));
+ 			}
  		}
  
  		/* Remember start of field on both input and output sides */