summaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/domains.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/domains.c')
-rw-r--r--src/backend/utils/adt/domains.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/src/backend/utils/adt/domains.c b/src/backend/utils/adt/domains.c
new file mode 100644
index 0000000000..051145f237
--- /dev/null
+++ b/src/backend/utils/adt/domains.c
@@ -0,0 +1,297 @@
+/*-------------------------------------------------------------------------
+ *
+ * domains.c
+ * I/O functions for domain types.
+ *
+ * The output functions for a domain type are just the same ones provided
+ * by its underlying base type. The input functions, however, must be
+ * prepared to apply any constraints defined by the type. So, we create
+ * special input functions that invoke the base type's input function
+ * and then check the constraints.
+ *
+ * The overhead required for constraint checking can be high, since examining
+ * the catalogs to discover the constraints for a given domain is not cheap.
+ * We have two mechanisms for minimizing this cost:
+ * 1. In a nest of domains, we flatten the checking of all the levels
+ * into just one operation.
+ * 2. We cache the list of constraint items in the FmgrInfo struct
+ * passed by the caller.
+ *
+ * We also have to create an EState to evaluate CHECK expressions in.
+ * Creating and destroying an EState is somewhat expensive, and so it's
+ * tempting to cache the EState too. However, that would mean that the
+ * EState never gets an explicit FreeExecutorState call, which is a bad idea
+ * because it risks leaking non-memory resources.
+ *
+ *
+ * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/utils/adt/domains.c,v 1.1 2006/04/05 22:11:55 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "commands/typecmds.h"
+#include "executor/executor.h"
+#include "lib/stringinfo.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+
+
+/*
+ * structure to cache state across multiple calls
+ */
+typedef struct DomainIOData
+{
+ Oid domain_type;
+ /* Data needed to call base type's input function */
+ Oid typiofunc;
+ Oid typioparam;
+ int32 typtypmod;
+ FmgrInfo proc;
+ /* List of constraint items to check */
+ List *constraint_list;
+} DomainIOData;
+
+
+/*
+ * domain_state_setup - initialize the cache for a new domain type.
+ */
+static void
+domain_state_setup(DomainIOData *my_extra, Oid domainType, bool binary,
+ MemoryContext mcxt)
+{
+ Oid baseType;
+ MemoryContext oldcontext;
+
+ /* Mark cache invalid */
+ my_extra->domain_type = InvalidOid;
+
+ /* Find out the base type */
+ my_extra->typtypmod = -1;
+ baseType = getBaseTypeAndTypmod(domainType, &my_extra->typtypmod);
+ if (baseType == domainType)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type %s is not a domain",
+ format_type_be(domainType))));
+
+ /* Look up underlying I/O function */
+ if (binary)
+ getTypeBinaryInputInfo(baseType,
+ &my_extra->typiofunc,
+ &my_extra->typioparam);
+ else
+ getTypeInputInfo(baseType,
+ &my_extra->typiofunc,
+ &my_extra->typioparam);
+ fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
+
+ /* Look up constraints for domain */
+ oldcontext = MemoryContextSwitchTo(mcxt);
+ my_extra->constraint_list = GetDomainConstraints(domainType);
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Mark cache valid */
+ my_extra->domain_type = domainType;
+}
+
+/*
+ * domain_check_input - apply the cached checks.
+ *
+ * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
+ */
+static void
+domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
+{
+ EState *estate = NULL;
+ ListCell *l;
+
+ foreach(l, my_extra->constraint_list)
+ {
+ DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+ switch (con->constrainttype)
+ {
+ case DOM_CONSTRAINT_NOTNULL:
+ if (isnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_NOT_NULL_VIOLATION),
+ errmsg("domain %s does not allow null values",
+ format_type_be(my_extra->domain_type))));
+ break;
+ case DOM_CONSTRAINT_CHECK:
+ {
+ ExprContext *econtext;
+ Datum conResult;
+ bool conIsNull;
+ Datum save_datum;
+ bool save_isNull;
+
+ if (estate == NULL)
+ estate = CreateExecutorState();
+ econtext = GetPerTupleExprContext(estate);
+
+ /*
+ * Set up value to be returned by CoerceToDomainValue
+ * nodes. We must save and restore prior setting of
+ * econtext's domainValue fields, in case this node is
+ * itself within a check expression for another domain.
+ */
+ save_datum = econtext->domainValue_datum;
+ save_isNull = econtext->domainValue_isNull;
+
+ econtext->domainValue_datum = value;
+ econtext->domainValue_isNull = isnull;
+
+ conResult = ExecEvalExprSwitchContext(con->check_expr,
+ econtext,
+ &conIsNull, NULL);
+
+ if (!conIsNull &&
+ !DatumGetBool(conResult))
+ ereport(ERROR,
+ (errcode(ERRCODE_CHECK_VIOLATION),
+ errmsg("value for domain %s violates check constraint \"%s\"",
+ format_type_be(my_extra->domain_type),
+ con->name)));
+ econtext->domainValue_datum = save_datum;
+ econtext->domainValue_isNull = save_isNull;
+
+ break;
+ }
+ default:
+ elog(ERROR, "unrecognized constraint type: %d",
+ (int) con->constrainttype);
+ break;
+ }
+ }
+
+ if (estate)
+ FreeExecutorState(estate);
+}
+
+
+/*
+ * domain_in - input routine for any domain type.
+ */
+Datum
+domain_in(PG_FUNCTION_ARGS)
+{
+ char *string;
+ Oid domainType;
+ DomainIOData *my_extra;
+ Datum value;
+
+ /*
+ * Since domain_in is not strict, we have to check for null inputs.
+ * The typioparam argument should never be null in normal system usage,
+ * but it could be null in a manual invocation --- if so, just return null.
+ */
+ if (PG_ARGISNULL(0))
+ string = NULL;
+ else
+ string = PG_GETARG_CSTRING(0);
+ if (PG_ARGISNULL(1))
+ PG_RETURN_NULL();
+ domainType = PG_GETARG_OID(1);
+
+ /*
+ * We arrange to look up the needed info just once per series of
+ * calls, assuming the domain type doesn't change underneath us.
+ */
+ my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
+ if (my_extra == NULL)
+ {
+ my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(DomainIOData));
+ domain_state_setup(my_extra, domainType, false,
+ fcinfo->flinfo->fn_mcxt);
+ fcinfo->flinfo->fn_extra = (void *) my_extra;
+ }
+ else if (my_extra->domain_type != domainType)
+ domain_state_setup(my_extra, domainType, false,
+ fcinfo->flinfo->fn_mcxt);
+
+ /*
+ * Invoke the base type's typinput procedure to convert the data.
+ */
+ value = InputFunctionCall(&my_extra->proc,
+ string,
+ my_extra->typioparam,
+ my_extra->typtypmod);
+
+ /*
+ * Do the necessary checks to ensure it's a valid domain value.
+ */
+ domain_check_input(value, (string == NULL), my_extra);
+
+ if (string == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(value);
+}
+
+/*
+ * domain_recv - binary input routine for any domain type.
+ */
+Datum
+domain_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf;
+ Oid domainType;
+ DomainIOData *my_extra;
+ Datum value;
+
+ /*
+ * Since domain_recv is not strict, we have to check for null inputs.
+ * The typioparam argument should never be null in normal system usage,
+ * but it could be null in a manual invocation --- if so, just return null.
+ */
+ if (PG_ARGISNULL(0))
+ buf = NULL;
+ else
+ buf = (StringInfo) PG_GETARG_POINTER(0);
+ if (PG_ARGISNULL(1))
+ PG_RETURN_NULL();
+ domainType = PG_GETARG_OID(1);
+
+ /*
+ * We arrange to look up the needed info just once per series of
+ * calls, assuming the domain type doesn't change underneath us.
+ */
+ my_extra = (DomainIOData *) fcinfo->flinfo->fn_extra;
+ if (my_extra == NULL)
+ {
+ my_extra = (DomainIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(DomainIOData));
+ domain_state_setup(my_extra, domainType, true,
+ fcinfo->flinfo->fn_mcxt);
+ fcinfo->flinfo->fn_extra = (void *) my_extra;
+ }
+ else if (my_extra->domain_type != domainType)
+ domain_state_setup(my_extra, domainType, true,
+ fcinfo->flinfo->fn_mcxt);
+
+ /*
+ * Invoke the base type's typreceive procedure to convert the data.
+ */
+ value = ReceiveFunctionCall(&my_extra->proc,
+ buf,
+ my_extra->typioparam,
+ my_extra->typtypmod);
+
+ /*
+ * Do the necessary checks to ensure it's a valid domain value.
+ */
+ domain_check_input(value, (buf == NULL), my_extra);
+
+ if (buf == NULL)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(value);
+}