summaryrefslogtreecommitdiff
path: root/src/backend/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/commands')
-rw-r--r--src/backend/commands/copy.c51
-rw-r--r--src/backend/commands/copyfrom.c20
-rw-r--r--src/backend/commands/copyfromparse.c85
3 files changed, 141 insertions, 15 deletions
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index f3bbd91fe3e..167d31a2d99 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -464,6 +464,12 @@ ProcessCopyOptions(ParseState *pstate,
errorConflictingDefElem(defel, pstate);
opts_out->null_print = defGetString(defel);
}
+ else if (strcmp(defel->defname, "default") == 0)
+ {
+ if (opts_out->default_print)
+ errorConflictingDefElem(defel, pstate);
+ opts_out->default_print = defGetString(defel);
+ }
else if (strcmp(defel->defname, "header") == 0)
{
if (header_specified)
@@ -577,6 +583,11 @@ ProcessCopyOptions(ParseState *pstate,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cannot specify NULL in BINARY mode")));
+ if (opts_out->binary && opts_out->default_print)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("cannot specify DEFAULT in BINARY mode")));
+
/* Set defaults for omitted options */
if (!opts_out->delim)
opts_out->delim = opts_out->csv_mode ? "," : "\t";
@@ -612,6 +623,17 @@ ProcessCopyOptions(ParseState *pstate,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("COPY null representation cannot use newline or carriage return")));
+ if (opts_out->default_print)
+ {
+ opts_out->default_print_len = strlen(opts_out->default_print);
+
+ if (strchr(opts_out->default_print, '\r') != NULL ||
+ strchr(opts_out->default_print, '\n') != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("COPY default representation cannot use newline or carriage return")));
+ }
+
/*
* Disallow unsafe delimiter characters in non-CSV mode. We can't allow
* backslash because it would be ambiguous. We can't allow the other
@@ -705,6 +727,35 @@ ProcessCopyOptions(ParseState *pstate,
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CSV quote character must not appear in the NULL specification")));
+
+ if (opts_out->default_print)
+ {
+ if (!is_from)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("COPY DEFAULT only available using COPY FROM")));
+
+ /* Don't allow the delimiter to appear in the default string. */
+ if (strchr(opts_out->default_print, opts_out->delim[0]) != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("COPY delimiter must not appear in the DEFAULT specification")));
+
+ /* Don't allow the CSV quote char to appear in the default string. */
+ if (opts_out->csv_mode &&
+ strchr(opts_out->default_print, opts_out->quote[0]) != NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("CSV quote character must not appear in the DEFAULT specification")));
+
+ /* Don't allow the NULL and DEFAULT string to be the same */
+ if (opts_out->null_print_len == opts_out->default_print_len &&
+ strncmp(opts_out->null_print, opts_out->default_print,
+ opts_out->null_print_len) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("NULL specification and DEFAULT specification cannot be the same")));
+ }
}
/*
diff --git a/src/backend/commands/copyfrom.c b/src/backend/commands/copyfrom.c
index 29cd1cf4a63..321a7fad854 100644
--- a/src/backend/commands/copyfrom.c
+++ b/src/backend/commands/copyfrom.c
@@ -1565,11 +1565,11 @@ BeginCopyFrom(ParseState *pstate,
&in_func_oid, &typioparams[attnum - 1]);
fmgr_info(in_func_oid, &in_functions[attnum - 1]);
- /* Get default info if needed */
- if (!list_member_int(cstate->attnumlist, attnum) && !att->attgenerated)
+ /* Get default info if available */
+ defexprs[attnum - 1] = NULL;
+
+ if (!att->attgenerated)
{
- /* attribute is NOT to be copied from input */
- /* use default value if one exists */
Expr *defexpr = (Expr *) build_column_default(cstate->rel,
attnum);
@@ -1579,9 +1579,15 @@ BeginCopyFrom(ParseState *pstate,
defexpr = expression_planner(defexpr);
/* Initialize executable expression in copycontext */
- defexprs[num_defaults] = ExecInitExpr(defexpr, NULL);
- defmap[num_defaults] = attnum - 1;
- num_defaults++;
+ defexprs[attnum - 1] = ExecInitExpr(defexpr, NULL);
+
+ /* if NOT copied from input */
+ /* use default value if one exists */
+ if (!list_member_int(cstate->attnumlist, attnum))
+ {
+ defmap[num_defaults] = attnum - 1;
+ num_defaults++;
+ }
/*
* If a default expression looks at the table being loaded,
diff --git a/src/backend/commands/copyfromparse.c b/src/backend/commands/copyfromparse.c
index 91b564c2bcb..c346486cd31 100644
--- a/src/backend/commands/copyfromparse.c
+++ b/src/backend/commands/copyfromparse.c
@@ -842,9 +842,10 @@ NextCopyFromRawFields(CopyFromState cstate, char ***fields, int *nfields)
/*
* Read next tuple from file for COPY FROM. Return false if no more tuples.
*
- * 'econtext' is used to evaluate default expression for each column not
- * read from the file. It can be NULL when no default values are used, i.e.
- * when all columns are read from the file.
+ * 'econtext' is used to evaluate default expression for each column that is
+ * either not read from the file or is using the DEFAULT option of COPY FROM.
+ * It can be NULL when no default values are used, i.e. when all columns are
+ * read from the file, and DEFAULT option is unset.
*
* 'values' and 'nulls' arrays must be the same length as columns of the
* relation passed to BeginCopyFrom. This function fills the arrays.
@@ -870,6 +871,7 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext,
/* Initialize all values for row to NULL */
MemSet(values, 0, num_phys_attrs * sizeof(Datum));
MemSet(nulls, true, num_phys_attrs * sizeof(bool));
+ cstate->defaults = (bool *) palloc0(num_phys_attrs * sizeof(bool));
if (!cstate->opts.binary)
{
@@ -938,12 +940,27 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext,
cstate->cur_attname = NameStr(att->attname);
cstate->cur_attval = string;
- values[m] = InputFunctionCall(&in_functions[m],
- string,
- typioparams[m],
- att->atttypmod);
+
if (string != NULL)
nulls[m] = false;
+
+ if (cstate->defaults[m])
+ {
+ /*
+ * The caller must supply econtext and have switched into the
+ * per-tuple memory context in it.
+ */
+ Assert(econtext != NULL);
+ Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory);
+
+ values[m] = ExecEvalExpr(defexprs[m], econtext, &nulls[m]);
+ }
+ else
+ values[m] = InputFunctionCall(&in_functions[m],
+ string,
+ typioparams[m],
+ att->atttypmod);
+
cstate->cur_attname = NULL;
cstate->cur_attval = NULL;
}
@@ -1019,10 +1036,12 @@ NextCopyFrom(CopyFromState cstate, ExprContext *econtext,
Assert(econtext != NULL);
Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory);
- values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext,
+ values[defmap[i]] = ExecEvalExpr(defexprs[defmap[i]], econtext,
&nulls[defmap[i]]);
}
+ pfree(cstate->defaults);
+
return true;
}
@@ -1663,6 +1682,31 @@ CopyReadAttributesText(CopyFromState cstate)
if (input_len == cstate->opts.null_print_len &&
strncmp(start_ptr, cstate->opts.null_print, input_len) == 0)
cstate->raw_fields[fieldno] = NULL;
+ /* Check whether raw input matched default marker */
+ else if (cstate->opts.default_print &&
+ input_len == cstate->opts.default_print_len &&
+ strncmp(start_ptr, cstate->opts.default_print, input_len) == 0)
+ {
+ /* fieldno is 0-indexed and attnum is 1-indexed */
+ int m = list_nth_int(cstate->attnumlist, fieldno) - 1;
+
+ if (cstate->defexprs[m] != NULL)
+ {
+ /* defaults contain entries for all physical attributes */
+ cstate->defaults[m] = true;
+ }
+ else
+ {
+ TupleDesc tupDesc = RelationGetDescr(cstate->rel);
+ Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ errmsg("unexpected DEFAULT in COPY data"),
+ errdetail("Column \"%s\" has no DEFAULT value.",
+ NameStr(att->attname))));
+ }
+ }
else
{
/*
@@ -1852,6 +1896,31 @@ endfield:
if (!saw_quote && input_len == cstate->opts.null_print_len &&
strncmp(start_ptr, cstate->opts.null_print, input_len) == 0)
cstate->raw_fields[fieldno] = NULL;
+ /* Check whether raw input matched default marker */
+ else if (cstate->opts.default_print &&
+ input_len == cstate->opts.default_print_len &&
+ strncmp(start_ptr, cstate->opts.default_print, input_len) == 0)
+ {
+ /* fieldno is 0-index and attnum is 1-index */
+ int m = list_nth_int(cstate->attnumlist, fieldno) - 1;
+
+ if (cstate->defexprs[m] != NULL)
+ {
+ /* defaults contain entries for all physical attributes */
+ cstate->defaults[m] = true;
+ }
+ else
+ {
+ TupleDesc tupDesc = RelationGetDescr(cstate->rel);
+ Form_pg_attribute att = TupleDescAttr(tupDesc, m);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_BAD_COPY_FILE_FORMAT),
+ errmsg("unexpected DEFAULT in COPY data"),
+ errdetail("Column \"%s\" has no DEFAULT value.",
+ NameStr(att->attname))));
+ }
+ }
fieldno++;
/* Done if we hit EOL instead of a delim */