diff options
Diffstat (limited to 'src/backend/commands')
| -rw-r--r-- | src/backend/commands/copy.c | 51 | ||||
| -rw-r--r-- | src/backend/commands/copyfrom.c | 20 | ||||
| -rw-r--r-- | src/backend/commands/copyfromparse.c | 85 |
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 */ |
