diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c index 8ce24e0..c0fc651 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.c +++ b/contrib/pg_stat_statements/pg_stat_statements.c @@ -2464,14 +2464,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node) JumbleExpr(jstate, (Node *) expr->aggfilter); } break; - case T_ArrayRef: + case T_SubscriptionRef: { - ArrayRef *aref = (ArrayRef *) node; + SubscriptionRef *sbsref = (SubscriptionRef *) node; - JumbleExpr(jstate, (Node *) aref->refupperindexpr); - JumbleExpr(jstate, (Node *) aref->reflowerindexpr); - JumbleExpr(jstate, (Node *) aref->refexpr); - JumbleExpr(jstate, (Node *) aref->refassgnexpr); + JumbleExpr(jstate, (Node *) sbsref->refupperindexpr); + JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr); + JumbleExpr(jstate, (Node *) sbsref->refexpr); + JumbleExpr(jstate, (Node *) sbsref->refassgnexpr); } break; case T_FuncExpr: diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c index 691658f..51d654d 100644 --- a/contrib/postgres_fdw/deparse.c +++ b/contrib/postgres_fdw/deparse.c @@ -137,7 +137,7 @@ static void deparseExpr(Expr *expr, deparse_expr_cxt *context); static void deparseVar(Var *node, deparse_expr_cxt *context); static void deparseConst(Const *node, deparse_expr_cxt *context); static void deparseParam(Param *node, deparse_expr_cxt *context); -static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context); +static void deparseSubscriptionRef(SubscriptionRef *node, deparse_expr_cxt *context); static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context); static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context); static void deparseOperatorName(StringInfo buf, Form_pg_operator opform); @@ -358,9 +358,9 @@ foreign_expr_walker(Node *node, state = FDW_COLLATE_UNSAFE; } break; - case T_ArrayRef: + case T_SubscriptionRef: { - ArrayRef *ar = (ArrayRef *) node; + SubscriptionRef *ar = (SubscriptionRef *) node; /* Assignment should not be in restrictions. */ if (ar->refassgnexpr != NULL) @@ -1822,8 +1822,8 @@ deparseExpr(Expr *node, deparse_expr_cxt *context) case T_Param: deparseParam((Param *) node, context); break; - case T_ArrayRef: - deparseArrayRef((ArrayRef *) node, context); + case T_SubscriptionRef: + deparseSubscriptionRef((SubscriptionRef *) node, context); break; case T_FuncExpr: deparseFuncExpr((FuncExpr *) node, context); @@ -2043,7 +2043,7 @@ deparseParam(Param *node, deparse_expr_cxt *context) * Deparse an array subscript expression. */ static void -deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context) +deparseSubscriptionRef(SubscriptionRef *node, deparse_expr_cxt *context) { StringInfo buf = context->buf; ListCell *lowlist_item; diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 29738b0..c6fe247 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -7056,6 +7056,13 @@ + typsubscription + regproc + pg_proc.oid + Custom subscription function with type-specific logic, or 0 if this type doesn't support subscription. + + + typdefaultbin pg_node_tree diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml index 69649a7..ff6a2c2 100644 --- a/doc/src/sgml/filelist.sgml +++ b/doc/src/sgml/filelist.sgml @@ -71,6 +71,7 @@ + diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml index 3cf78d6..399e4f3 100644 --- a/doc/src/sgml/json.sgml +++ b/doc/src/sgml/json.sgml @@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu compared using the default database collation. + + + JSON subscription + + JSONB data type support array-style subscription expressions to extract or update particular element. An example of subscription syntax: + +-- Extract value by key +SELECT ('{"a": 1}'::jsonb)['a']; + +-- Extract nested value by key path +SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c']; + +-- Extract element by index +SELECT ('[1, "2", null]'::jsonb)['1']; + +-- Update value by key +UPDATE table_name set jsonb_field['key'] = 1; + +-- Select records using where clause with subscription +SELECT * from table_name where jsonb_field['key'] = '"value"'; + + + + + diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index 5a09f19..8588065 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -54,6 +54,7 @@ CREATE TYPE name ( [ , ELEMENT = element ] [ , DELIMITER = delimiter ] [ , COLLATABLE = collatable ] + [ , SUBSCRIPTION = subscription_function ] ) CREATE TYPE name @@ -194,7 +195,8 @@ CREATE TYPE name send_function, type_modifier_input_function, type_modifier_output_function and - analyze_function + analyze_function, + subscription_function are optional. Generally these functions have to be coded in C or another low-level language. @@ -451,6 +453,22 @@ CREATE TYPE name make use of the collation information; this does not happen automatically merely by marking the type collatable. + + + The optional + subscription_function + contains type-specific logic for subscription of the data type. + By default, there is no such function, which means that the data + type doesn't support subscription. The subscription function must be + declared to take a single argument of type internal, and return + a internal result. There are two examples of implementation for + subscription function in case of array + (array_subscription) + and jsonb + (jsonb_subscription) + types in src/backend/utils/adt/arrayfuncs.c and + src/backend/utils/adt/jsonfuncs.c corresponding. + @@ -766,6 +784,16 @@ CREATE TYPE name + + + subscription_function + + + The name of a function that contains type-specific subscription logic for + the data type. + + + diff --git a/doc/src/sgml/xsubscription.sgml b/doc/src/sgml/xsubscription.sgml new file mode 100644 index 0000000..255ef66 --- /dev/null +++ b/doc/src/sgml/xsubscription.sgml @@ -0,0 +1,98 @@ + + + + User-defined subscription procedure + + + custom subscription + + When you define a new base type, you can also specify a custom procedure + to handle subscription expressions. It should contains logic for verification + and for extraction or update your data. For instance: + +containerSource; + + // Some extraction or update logic based on sbsdata +} + +Datum +custom_subscription_prepare(PG_FUNCTION_ARGS) +{ + SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0); + + // Some verifications or type coersion + + PG_RETURN_POINTER(sbsref); +} + +PG_FUNCTION_INFO_V1(custom_subscription); + +Datum +custom_subscription(PG_FUNCTION_ARGS) +{ + int op_type = PG_GETARG_INT32(0); + FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1, + fcinfo->nargs); + + if (op_type & SBS_VALIDATION) + return custom_subscription_prepare(&target_fcinfo); + + if (op_type & SBS_EXEC) + return custom_subscription_evaluate(&target_fcinfo); + + elog(ERROR, "incorrect op_type for subscription function: %d", op_type); +}]]> + + + Then you can define a subscription procedure and a custom data type: + + +CREATE FUNCTION custom_subscription(internal) + RETURNS internal + AS 'filename' + LANGUAGE C IMMUTABLE STRICT; + +CREATE TYPE custom ( + internallength = 4, + input = custom_in, + output = custom_out, + subscription = custom_subscription +); + + + and use it as usual: + + +CREATE TABLE test_subscription ( + data custom, +); + +INSERT INTO test_subscription VALUES ('(1, 2)'); + +SELECT data[0] from test_subscription; + +UPDATE test_subscription SET data[1] = 3; + + + + + + The examples of custom subscription implementation can be found in + subscription.sql and subscription.c + in the src/tutorial directory of the source distribution. + See the README file in that directory for instructions + about running the examples. + + + diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index dbd6094..73f89d3 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -977,7 +977,8 @@ AddNewRelationType(const char *typeName, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* rowtypes never have a collation */ + InvalidOid, /* rowtypes never have a collation */ + InvalidOid); /* typsubscription - none */ } /* -------------------------------- @@ -1244,7 +1245,8 @@ heap_create_with_catalog(const char *relname, -1, /* typmod */ 0, /* array dimensions for typBaseType */ false, /* Type NOT NULL */ - InvalidOid); /* rowtypes never have a collation */ + InvalidOid, /* rowtypes never have a collation */ + F_ARRAY_SUBSCRIPTION); /* array implementation */ pfree(relarrayname); } diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 4b2d281..dabcb64 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1); values[Anum_pg_type_typndims - 1] = Int32GetDatum(0); values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid); + values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(InvalidOid); nulls[Anum_pg_type_typdefaultbin - 1] = true; nulls[Anum_pg_type_typdefault - 1] = true; nulls[Anum_pg_type_typacl - 1] = true; @@ -166,6 +167,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) false, InvalidOid, InvalidOid, + InvalidOid, NULL, false); @@ -224,7 +226,8 @@ TypeCreate(Oid newTypeOid, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ bool typeNotNull, - Oid typeCollation) + Oid typeCollation, + Oid subscriptionProcedure) { Relation pg_type_desc; Oid typeObjectId; @@ -364,6 +367,7 @@ TypeCreate(Oid newTypeOid, values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod); values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims); values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation); + values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(subscriptionProcedure); /* * initialize the default binary value for this type. Check for nulls of @@ -484,6 +488,7 @@ TypeCreate(Oid newTypeOid, isImplicitArray, baseType, typeCollation, + subscriptionProcedure, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), @@ -530,6 +535,7 @@ GenerateTypeDependencies(Oid typeNamespace, bool isImplicitArray, Oid baseType, Oid typeCollation, + Oid subscriptionProcedure, Node *defaultExpr, bool rebuild) { @@ -682,6 +688,14 @@ GenerateTypeDependencies(Oid typeNamespace, /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); + + if (OidIsValid(subscriptionProcedure)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = subscriptionProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 056933a..c3497e0 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -94,6 +94,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid); static Oid findTypeTypmodinFunction(List *procname); static Oid findTypeTypmodoutFunction(List *procname); static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid); +static Oid findTypeSubscriptionFunction(List *procname, Oid typeOid); static Oid findRangeSubOpclass(List *opcname, Oid subtype); static Oid findRangeCanonicalFunction(List *procname, Oid typeOid); static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype); @@ -123,6 +124,7 @@ DefineType(ParseState *pstate, List *names, List *parameters) List *typmodinName = NIL; List *typmodoutName = NIL; List *analyzeName = NIL; + List *subscriptionName = NIL; char category = TYPCATEGORY_USER; bool preferred = false; char delimiter = DEFAULT_TYPDELIM; @@ -141,6 +143,7 @@ DefineType(ParseState *pstate, List *names, List *parameters) DefElem *typmodinNameEl = NULL; DefElem *typmodoutNameEl = NULL; DefElem *analyzeNameEl = NULL; + DefElem *subscriptionNameEl = NULL; DefElem *categoryEl = NULL; DefElem *preferredEl = NULL; DefElem *delimiterEl = NULL; @@ -163,6 +166,7 @@ DefineType(ParseState *pstate, List *names, List *parameters) Oid resulttype; ListCell *pl; ObjectAddress address; + Oid subscriptionOid = InvalidOid; /* * As of Postgres 8.4, we require superuser privilege to create a base @@ -262,6 +266,9 @@ DefineType(ParseState *pstate, List *names, List *parameters) else if (pg_strcasecmp(defel->defname, "analyze") == 0 || pg_strcasecmp(defel->defname, "analyse") == 0) defelp = &analyzeNameEl; + else if (pg_strcasecmp(defel->defname, "subscription") == 0 || + pg_strcasecmp(defel->defname, "subscription") == 0) + defelp = &subscriptionNameEl; else if (pg_strcasecmp(defel->defname, "category") == 0) defelp = &categoryEl; else if (pg_strcasecmp(defel->defname, "preferred") == 0) @@ -332,6 +339,8 @@ DefineType(ParseState *pstate, List *names, List *parameters) typmodoutName = defGetQualifiedName(typmodoutNameEl); if (analyzeNameEl) analyzeName = defGetQualifiedName(analyzeNameEl); + if (subscriptionNameEl) + subscriptionName = defGetQualifiedName(subscriptionNameEl); if (categoryEl) { char *p = defGetString(categoryEl); @@ -513,6 +522,9 @@ DefineType(ParseState *pstate, List *names, List *parameters) if (analyzeName) analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid); + if (subscriptionName) + subscriptionOid = findTypeSubscriptionFunction(subscriptionName, typoid); + /* * Check permissions on functions. We choose to require the creator/owner * of a type to also own the underlying functions. Since creating a type @@ -632,7 +644,8 @@ DefineType(ParseState *pstate, List *names, List *parameters) -1, /* typMod (Domains only) */ 0, /* Array Dimensions of typbasetype */ false, /* Type NOT NULL */ - collation); /* type's collation */ + collation, /* type's collation */ + subscriptionOid); /* subscription procedure */ Assert(typoid == address.objectId); /* @@ -673,7 +686,8 @@ DefineType(ParseState *pstate, List *names, List *parameters) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - collation); /* type's collation */ + collation, /* type's collation */ + F_ARRAY_SUBSCRIPTION); pfree(array_type); @@ -735,6 +749,7 @@ DefineDomain(CreateDomainStmt *stmt) Oid receiveProcedure; Oid sendProcedure; Oid analyzeProcedure; + Oid subscriptionProcedure; bool byValue; char category; char delimiter; @@ -858,6 +873,9 @@ DefineDomain(CreateDomainStmt *stmt) /* Analysis function */ analyzeProcedure = baseType->typanalyze; + /* Subscription function */ + subscriptionProcedure = baseType->typsubscription; + /* Inherited default value */ datum = SysCacheGetAttr(TYPEOID, typeTup, Anum_pg_type_typdefault, &isnull); @@ -1059,7 +1077,8 @@ DefineDomain(CreateDomainStmt *stmt) basetypeMod, /* typeMod value */ typNDims, /* Array dimensions for base type */ typNotNull, /* Type NOT NULL */ - domaincoll); /* type's collation */ + domaincoll, /* type's collation */ + subscriptionProcedure); /* subscription procedure */ /* * Process constraints which refer to the domain ID returned by TypeCreate @@ -1171,7 +1190,8 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation */ + InvalidOid, /* type's collation */ + InvalidOid); /* typsubscription - none */ /* Enter the enum's values into pg_enum */ EnumValuesCreate(enumTypeAddr.objectId, stmt->vals); @@ -1211,7 +1231,8 @@ DefineEnum(CreateEnumStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation */ + InvalidOid, /* type's collation */ + F_ARRAY_SUBSCRIPTION); /* array subscription implementation */ pfree(enumArrayName); @@ -1499,7 +1520,8 @@ DefineRange(CreateRangeStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* type's collation (ranges never have one) */ + InvalidOid, /* type's collation (ranges never have one) */ + InvalidOid); /* typsubscription - none */ Assert(typoid == address.objectId); /* Create the entry in pg_range */ @@ -1541,7 +1563,8 @@ DefineRange(CreateRangeStmt *stmt) -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ false, /* Type NOT NULL */ - InvalidOid); /* typcollation */ + InvalidOid, /* typcollation */ + F_ARRAY_SUBSCRIPTION); /* array subscription implementation */ pfree(rangeArrayName); @@ -1885,6 +1908,33 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid) return procOid; } +static Oid +findTypeSubscriptionFunction(List *procname, Oid typeOid) +{ + Oid argList[1]; + Oid procOid; + + /* + * Analyze functions always take one INTERNAL argument and return INTERNAL. + */ + argList[0] = INTERNALOID; + + procOid = LookupFuncName(procname, 1, argList, true); + if (!OidIsValid(procOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(procname, 1, NIL, argList)))); + + if (get_func_rettype(procOid) != INTERNALOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("type subscription function %s must return type %s", + NameListToString(procname), "internal"))); + + return procOid; +} + /* * Find suitable support functions and opclasses for a range type. */ @@ -2239,6 +2289,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) false, /* a domain isn't an implicit array */ typTup->typbasetype, typTup->typcollation, + typTup->typsubscription, defaultExpr, true); /* Rebuild is true */ diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 743e7d6..da70901 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -55,6 +55,7 @@ #include "utils/builtins.h" #include "utils/date.h" #include "utils/lsyscache.h" +#include "utils/syscache.h" #include "utils/memutils.h" #include "utils/timestamp.h" #include "utils/typcache.h" @@ -62,10 +63,9 @@ /* static function decls */ -static Datum ExecEvalArrayRef(ArrayRefExprState *astate, +static Datum ExecEvalSubscriptionRef(SubscriptionRefExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static bool isAssignmentIndirectionExpr(ExprState *exprstate); static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -251,39 +251,50 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate, /*---------- - * ExecEvalArrayRef + * ExecEvalSubscriptionRef * - * This function takes an ArrayRef and returns the extracted Datum - * if it's a simple reference, or the modified array value if it's - * an array assignment (i.e., array element or slice insertion). + * This function takes a SubscriptionRef, extracts all information required + * for subscription and pass it to a particular subscription procedure, + * specified for this data type. As a result the extracted Datum will be + * returned if it's a simple reference, or the modified containers value if + * it's an containers assignment (i.e., containers element or slice + * insertion). * * NOTE: if we get a NULL result from a subscript expression, we return NULL - * when it's an array reference, or raise an error when it's an assignment. + * when it's an containers reference, or raise an error when it's an assignment. *---------- */ + static Datum -ExecEvalArrayRef(ArrayRefExprState *astate, - ExprContext *econtext, - bool *isNull, - ExprDoneCond *isDone) +ExecEvalSubscriptionRef(SubscriptionRefExprState *sbstate, + ExprContext *econtext, + bool *isNull, + ExprDoneCond *isDone) { - ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr; - Datum array_source; - bool isAssignment = (arrayRef->refassgnexpr != NULL); - bool eisnull; - ListCell *l; - int i = 0, - j = 0; - IntArray upper, - lower; - bool upperProvided[MAXDIM], - lowerProvided[MAXDIM]; - int *lIndex; - - array_source = ExecEvalExpr(astate->refexpr, - econtext, - isNull, - isDone); + SubscriptionRef *sbsRef = (SubscriptionRef *) sbstate->xprstate.expr; + Oid containerType; + RegProcedure typsubscription; + bool isAssignment = (sbsRef->refassgnexpr != NULL); + bool eisnull; + Datum *upper = NULL, + *lower = NULL; + ListCell *l; + int i = 0, + j = 0; + SubscriptionExecData sbsdata; + bool upperProvided[MAXDIM], + lowerProvided[MAXDIM]; + + if (sbstate->refupperindexpr != NULL) + upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *)); + + if (sbstate->reflowerindexpr != NULL) + lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *)); + + sbsdata.xprcontext = econtext; + sbsdata.isNull = isNull; + sbsdata.containerSource = ExecEvalExpr(sbstate->refexpr, econtext, + isNull, isDone); /* * If refexpr yields NULL, and it's a fetch, then result is NULL. In the @@ -297,51 +308,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate, return (Datum) NULL; } - foreach(l, astate->refupperindexpr) + foreach(l, sbstate->refupperindexpr) { ExprState *eltstate = (ExprState *) lfirst(l); if (i >= MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)", i + 1, MAXDIM))); if (eltstate == NULL) { - /* Slice bound is omitted, so use array's upper bound */ - Assert(astate->reflowerindexpr != NIL); + /* Slice bound is omitted, so use containers's upper bound */ + Assert(sbstate->reflowerindexpr != NIL); upperProvided[i++] = false; continue; } + upperProvided[i] = true; + upper[i++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL); - upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, - econtext, - &eisnull, - NULL)); /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { if (isAssignment) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("array subscript in assignment must not be null"))); + errmsg("container subscript in assignment must not be null"))); *isNull = true; return (Datum) NULL; } } - if (astate->reflowerindexpr != NIL) + if (sbstate->reflowerindexpr != NIL) { - foreach(l, astate->reflowerindexpr) + foreach(l, sbstate->reflowerindexpr) { ExprState *eltstate = (ExprState *) lfirst(l); if (j >= MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), - errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", + errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)", j + 1, MAXDIM))); if (eltstate == NULL) @@ -350,191 +359,45 @@ ExecEvalArrayRef(ArrayRefExprState *astate, lowerProvided[j++] = false; continue; } + lowerProvided[j] = true; + lower[j++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL); - lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, - econtext, - &eisnull, - NULL)); /* If any index expr yields NULL, result is NULL or error */ if (eisnull) { if (isAssignment) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), - errmsg("array subscript in assignment must not be null"))); + errmsg("container subscript in assignment must not be null"))); *isNull = true; return (Datum) NULL; } } + /* this can't happen unless parser messed up */ if (i != j) elog(ERROR, "upper and lower index lists are not same length"); - lIndex = lower.indx; - } - else - lIndex = NULL; - - if (isAssignment) - { - Datum sourceData; - Datum save_datum; - bool save_isNull; - - /* - * We might have a nested-assignment situation, in which the - * refassgnexpr is itself a FieldStore or ArrayRef that needs to - * obtain and modify the previous value of the array element or slice - * being replaced. If so, we have to extract that value from the - * array and pass it down via the econtext's caseValue. It's safe to - * reuse the CASE mechanism because there cannot be a CASE between - * here and where the value would be needed, and an array assignment - * can't be within a CASE either. (So saving and restoring the - * caseValue is just paranoia, but let's do it anyway.) - * - * Since fetching the old element might be a nontrivial expense, do it - * only if the argument appears to actually need it. - */ - save_datum = econtext->caseValue_datum; - save_isNull = econtext->caseValue_isNull; - - if (isAssignmentIndirectionExpr(astate->refassgnexpr)) - { - if (*isNull) - { - /* whole array is null, so any element or slice is too */ - econtext->caseValue_datum = (Datum) 0; - econtext->caseValue_isNull = true; - } - else if (lIndex == NULL) - { - econtext->caseValue_datum = - array_get_element(array_source, i, - upper.indx, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign, - &econtext->caseValue_isNull); - } - else - { - econtext->caseValue_datum = - array_get_slice(array_source, i, - upper.indx, lower.indx, - upperProvided, lowerProvided, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); - econtext->caseValue_isNull = false; - } - } - else - { - /* argument shouldn't need caseValue, but for safety set it null */ - econtext->caseValue_datum = (Datum) 0; - econtext->caseValue_isNull = true; - } - - /* - * Evaluate the value to be assigned into the array. - */ - sourceData = ExecEvalExpr(astate->refassgnexpr, - econtext, - &eisnull, - NULL); - - econtext->caseValue_datum = save_datum; - econtext->caseValue_isNull = save_isNull; - - /* - * For an assignment to a fixed-length array type, both the original - * array and the value to be assigned into it must be non-NULL, else - * we punt and return the original array. - */ - if (astate->refattrlength > 0) /* fixed-length array? */ - if (eisnull || *isNull) - return array_source; - - /* - * For assignment to varlena arrays, we handle a NULL original array - * by substituting an empty (zero-dimensional) array; insertion of the - * new element will result in a singleton array value. It does not - * matter whether the new element is NULL. - */ - if (*isNull) - { - array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype)); - *isNull = false; - } - - if (lIndex == NULL) - return array_set_element(array_source, i, - upper.indx, - sourceData, - eisnull, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); - else - return array_set_slice(array_source, i, - upper.indx, lower.indx, - upperProvided, lowerProvided, - sourceData, - eisnull, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); } - if (lIndex == NULL) - return array_get_element(array_source, i, - upper.indx, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign, - isNull); - else - return array_get_slice(array_source, i, - upper.indx, lower.indx, - upperProvided, lowerProvided, - astate->refattrlength, - astate->refelemlength, - astate->refelembyval, - astate->refelemalign); -} + sbsdata.upper = upper; + sbsdata.upperProvided = upperProvided; + sbsdata.lower = lower; + sbsdata.lowerProvided = lowerProvided; + sbsdata.indexprNumber = i; -/* - * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef - * that might need the old element value passed down? - * - * (We could use this in ExecEvalFieldStore too, but in that case passing - * the old value is so cheap there's no need.) - */ -static bool -isAssignmentIndirectionExpr(ExprState *exprstate) -{ - if (exprstate == NULL) - return false; /* just paranoia */ - if (IsA(exprstate, FieldStoreState)) - { - FieldStore *fstore = (FieldStore *) exprstate->expr; + containerType = getBaseTypeAndTypmod(sbsRef->refcontainertype, &sbsRef->reftypmod); + typsubscription = get_typsubscription(containerType); - if (fstore->arg && IsA(fstore->arg, CaseTestExpr)) - return true; - } - else if (IsA(exprstate, ArrayRefExprState)) - { - ArrayRef *arrayRef = (ArrayRef *) exprstate->expr; + if (!OidIsValid(typsubscription)) + /* this can't happen */ + elog(ERROR, "can not find subscription procedure for type %s", + format_type_be(containerType)); - if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr)) - return true; - } - return false; + return OidFunctionCall3(typsubscription, + Int32GetDatum(SBS_EXEC), + PointerGetDatum(sbstate), + PointerGetDatum(&sbsdata)); } /* ---------------------------------------------------------------- @@ -4332,7 +4195,7 @@ ExecEvalFieldStore(FieldStoreState *fstate, /* * Use the CaseTestExpr mechanism to pass down the old value of the * field being replaced; this is needed in case the newval is itself a - * FieldStore or ArrayRef that has to obtain and modify the old value. + * FieldStore or SubscriptionRef that has to obtain and modify the old value. * It's safe to reuse the CASE mechanism because there cannot be a * CASE between here and where the value would be needed, and a field * assignment can't be within a CASE either. (So saving and restoring @@ -4695,25 +4558,21 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) wfstate; } break; - case T_ArrayRef: + case T_SubscriptionRef: { - ArrayRef *aref = (ArrayRef *) node; - ArrayRefExprState *astate = makeNode(ArrayRefExprState); + SubscriptionRef *sbsref = (SubscriptionRef *) node; + SubscriptionRefExprState *astate = makeNode(SubscriptionRefExprState); - astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef; + astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalSubscriptionRef; astate->refupperindexpr = (List *) - ExecInitExpr((Expr *) aref->refupperindexpr, parent); + ExecInitExpr((Expr *) sbsref->refupperindexpr, parent); astate->reflowerindexpr = (List *) - ExecInitExpr((Expr *) aref->reflowerindexpr, parent); - astate->refexpr = ExecInitExpr(aref->refexpr, parent); - astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr, + ExecInitExpr((Expr *) sbsref->reflowerindexpr, parent); + astate->refexpr = ExecInitExpr(sbsref->refexpr, parent); + astate->refassgnexpr = ExecInitExpr(sbsref->refassgnexpr, parent); /* do one-time catalog lookups for type info */ - astate->refattrlength = get_typlen(aref->refarraytype); - get_typlenbyvalalign(aref->refelemtype, - &astate->refelemlength, - &astate->refelembyval, - &astate->refelemalign); + astate->refattrlength = get_typlen(sbsref->refcontainertype); state = (ExprState *) astate; } break; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 71714bc..7e0f48b 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1292,14 +1292,14 @@ _copyWindowFunc(const WindowFunc *from) } /* - * _copyArrayRef + * _copySubscriptionRef */ -static ArrayRef * -_copyArrayRef(const ArrayRef *from) +static SubscriptionRef * +_copySubscriptionRef(const SubscriptionRef *from) { - ArrayRef *newnode = makeNode(ArrayRef); + SubscriptionRef *newnode = makeNode(SubscriptionRef); - COPY_SCALAR_FIELD(refarraytype); + COPY_SCALAR_FIELD(refcontainertype); COPY_SCALAR_FIELD(refelemtype); COPY_SCALAR_FIELD(reftypmod); COPY_SCALAR_FIELD(refcollid); @@ -4467,8 +4467,8 @@ copyObject(const void *from) case T_WindowFunc: retval = _copyWindowFunc(from); break; - case T_ArrayRef: - retval = _copyArrayRef(from); + case T_SubscriptionRef: + retval = _copySubscriptionRef(from); break; case T_FuncExpr: retval = _copyFuncExpr(from); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 29a090f..c680122 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -244,9 +244,9 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b) } static bool -_equalArrayRef(const ArrayRef *a, const ArrayRef *b) +_equalSubscriptionRef(const SubscriptionRef *a, const SubscriptionRef *b) { - COMPARE_SCALAR_FIELD(refarraytype); + COMPARE_SCALAR_FIELD(refcontainertype); COMPARE_SCALAR_FIELD(refelemtype); COMPARE_SCALAR_FIELD(reftypmod); COMPARE_SCALAR_FIELD(refcollid); @@ -2779,8 +2779,8 @@ equal(const void *a, const void *b) case T_WindowFunc: retval = _equalWindowFunc(a, b); break; - case T_ArrayRef: - retval = _equalArrayRef(a, b); + case T_SubscriptionRef: + retval = _equalSubscriptionRef(a, b); break; case T_FuncExpr: retval = _equalFuncExpr(a, b); diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 3997441..a02719f 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -66,13 +66,13 @@ exprType(const Node *expr) case T_WindowFunc: type = ((const WindowFunc *) expr)->wintype; break; - case T_ArrayRef: + case T_SubscriptionRef: { - const ArrayRef *arrayref = (const ArrayRef *) expr; + const SubscriptionRef *arrayref = (const SubscriptionRef *) expr; /* slice and/or store operations yield the array type */ if (arrayref->reflowerindexpr || arrayref->refassgnexpr) - type = arrayref->refarraytype; + type = arrayref->refcontainertype; else type = arrayref->refelemtype; } @@ -284,9 +284,9 @@ exprTypmod(const Node *expr) return ((const Const *) expr)->consttypmod; case T_Param: return ((const Param *) expr)->paramtypmod; - case T_ArrayRef: + case T_SubscriptionRef: /* typmod is the same for array or element */ - return ((const ArrayRef *) expr)->reftypmod; + return ((const SubscriptionRef *) expr)->reftypmod; case T_FuncExpr: { int32 coercedTypmod; @@ -772,8 +772,8 @@ exprCollation(const Node *expr) case T_WindowFunc: coll = ((const WindowFunc *) expr)->wincollid; break; - case T_ArrayRef: - coll = ((const ArrayRef *) expr)->refcollid; + case T_SubscriptionRef: + coll = ((const SubscriptionRef *) expr)->refcollid; break; case T_FuncExpr: coll = ((const FuncExpr *) expr)->funccollid; @@ -1014,8 +1014,8 @@ exprSetCollation(Node *expr, Oid collation) case T_WindowFunc: ((WindowFunc *) expr)->wincollid = collation; break; - case T_ArrayRef: - ((ArrayRef *) expr)->refcollid = collation; + case T_SubscriptionRef: + ((SubscriptionRef *) expr)->refcollid = collation; break; case T_FuncExpr: ((FuncExpr *) expr)->funccollid = collation; @@ -1237,9 +1237,9 @@ exprLocation(const Node *expr) /* function name should always be the first thing */ loc = ((const WindowFunc *) expr)->location; break; - case T_ArrayRef: + case T_SubscriptionRef: /* just use array argument's location */ - loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr); + loc = exprLocation((Node *) ((const SubscriptionRef *) expr)->refexpr); break; case T_FuncExpr: { @@ -1926,21 +1926,21 @@ expression_tree_walker(Node *node, return true; } break; - case T_ArrayRef: + case T_SubscriptionRef: { - ArrayRef *aref = (ArrayRef *) node; + SubscriptionRef *sbsref = (SubscriptionRef *) node; /* recurse directly for upper/lower array index lists */ - if (expression_tree_walker((Node *) aref->refupperindexpr, + if (expression_tree_walker((Node *) sbsref->refupperindexpr, walker, context)) return true; - if (expression_tree_walker((Node *) aref->reflowerindexpr, + if (expression_tree_walker((Node *) sbsref->reflowerindexpr, walker, context)) return true; /* walker must see the refexpr and refassgnexpr, however */ - if (walker(aref->refexpr, context)) + if (walker(sbsref->refexpr, context)) return true; - if (walker(aref->refassgnexpr, context)) + if (walker(sbsref->refassgnexpr, context)) return true; } break; @@ -2515,12 +2515,12 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; - case T_ArrayRef: + case T_SubscriptionRef: { - ArrayRef *arrayref = (ArrayRef *) node; - ArrayRef *newnode; + SubscriptionRef *arrayref = (SubscriptionRef *) node; + SubscriptionRef *newnode; - FLATCOPY(newnode, arrayref, ArrayRef); + FLATCOPY(newnode, arrayref, SubscriptionRef); MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr, List *); MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr, diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index ae86954..4616c4d 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1062,11 +1062,11 @@ _outWindowFunc(StringInfo str, const WindowFunc *node) } static void -_outArrayRef(StringInfo str, const ArrayRef *node) +_outSubscriptionRef(StringInfo str, const SubscriptionRef *node) { - WRITE_NODE_TYPE("ARRAYREF"); + WRITE_NODE_TYPE("SUBSCRIPTIONREF"); - WRITE_OID_FIELD(refarraytype); + WRITE_OID_FIELD(refcontainertype); WRITE_OID_FIELD(refelemtype); WRITE_INT_FIELD(reftypmod); WRITE_OID_FIELD(refcollid); @@ -3445,8 +3445,8 @@ outNode(StringInfo str, const void *obj) case T_WindowFunc: _outWindowFunc(str, obj); break; - case T_ArrayRef: - _outArrayRef(str, obj); + case T_SubscriptionRef: + _outSubscriptionRef(str, obj); break; case T_FuncExpr: _outFuncExpr(str, obj); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 917e6c8..280ae6e 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -606,14 +606,14 @@ _readWindowFunc(void) } /* - * _readArrayRef + * _readSubscriptionRef */ -static ArrayRef * -_readArrayRef(void) +static SubscriptionRef * +_readSubscriptionRef(void) { - READ_LOCALS(ArrayRef); + READ_LOCALS(SubscriptionRef); - READ_OID_FIELD(refarraytype); + READ_OID_FIELD(refcontainertype); READ_OID_FIELD(refelemtype); READ_INT_FIELD(reftypmod); READ_OID_FIELD(refcollid); @@ -2319,8 +2319,8 @@ parseNodeString(void) return_value = _readGroupingFunc(); else if (MATCH("WINDOWFUNC", 10)) return_value = _readWindowFunc(); - else if (MATCH("ARRAYREF", 8)) - return_value = _readArrayRef(); + else if (MATCH("SUBSCRIPTIONREF", 15)) + return_value = _readSubscriptionRef(); else if (MATCH("FUNCEXPR", 8)) return_value = _readFuncExpr(); else if (MATCH("NAMEDARGEXPR", 12)) diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 663ffe0..2c7b738 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -1329,10 +1329,10 @@ contain_nonstrict_functions_walker(Node *node, void *context) /* a window function could return non-null with null input */ return true; } - if (IsA(node, ArrayRef)) + if (IsA(node, SubscriptionRef)) { /* array assignment is nonstrict, but subscripting is strict */ - if (((ArrayRef *) node)->refassgnexpr != NULL) + if (((SubscriptionRef *) node)->refassgnexpr != NULL) return true; /* else fall through to check args */ } @@ -1512,7 +1512,7 @@ contain_leaked_vars_walker(Node *node, void *context) case T_Var: case T_Const: case T_Param: - case T_ArrayRef: + case T_SubscriptionRef: case T_ArrayExpr: case T_FieldSelect: case T_FieldStore: @@ -3573,7 +3573,7 @@ eval_const_expressions_mutator(Node *node, * For any node type not handled above, we recurse using * expression_tree_mutator, which will copy the node unchanged but try to * simplify its arguments (if any) using this routine. For example: we - * cannot eliminate an ArrayRef node, but we might be able to simplify + * cannot eliminate an SubscriptionRef node, but we might be able to simplify * constant expressions in its subscripts. */ return expression_tree_mutator(node, eval_const_expressions_mutator, diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 6901e08..c8f2eae 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -917,13 +917,13 @@ transformInsertRow(ParseState *pstate, List *exprlist, expr = (Expr *) linitial(fstore->newvals); } - else if (IsA(expr, ArrayRef)) + else if (IsA(expr, SubscriptionRef)) { - ArrayRef *aref = (ArrayRef *) expr; + SubscriptionRef *sbsref = (SubscriptionRef *) expr; - if (aref->refassgnexpr == NULL) + if (sbsref->refassgnexpr == NULL) break; - expr = aref->refassgnexpr; + expr = sbsref->refassgnexpr; } else break; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 63f7965..3005e47 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -461,13 +461,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) /* process subscripts before this field selection */ if (subscripts) - result = (Node *) transformArraySubscripts(pstate, - result, - exprType(result), - InvalidOid, - exprTypmod(result), - subscripts, - NULL); + result = (Node *) transformContainerSubscripts(pstate, + result, + exprType(result), + InvalidOid, + exprTypmod(result), + subscripts, + NULL); subscripts = NIL; newresult = ParseFuncOrColumn(pstate, @@ -482,13 +482,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) } /* process trailing subscripts, if any */ if (subscripts) - result = (Node *) transformArraySubscripts(pstate, - result, - exprType(result), - InvalidOid, - exprTypmod(result), - subscripts, - NULL); + result = (Node *) transformContainerSubscripts(pstate, + result, + exprType(result), + InvalidOid, + exprTypmod(result), + subscripts, + NULL); return result; } diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 62d2f71..948fb8b 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -201,18 +201,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location) /* * transformArrayType() - * Identify the types involved in a subscripting operation + * Identify the types involved in a subscripting operation for array * * On entry, arrayType/arrayTypmod identify the type of the input value * to be subscripted (which could be a domain type). These are modified * if necessary to identify the actual array type and typmod, and the * array's element type is returned. An error is thrown if the input isn't * an array type. + * + * NOTE: This part of type-specific code is not separated into type-specific + * subscription procedure for now, but it does not affect on the whole logic, + * since InvalidOid will be return in case of other types not an error. + * An error will appears only if a subscription procedure is not defined. */ Oid -transformArrayType(Oid *arrayType, int32 *arrayTypmod) +transformArrayType(Oid *containerType, int32 *containerTypmod) { - Oid origArrayType = *arrayType; Oid elementType; HeapTuple type_tuple_array; Form_pg_type type_struct_array; @@ -224,7 +228,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) * itself. (Note that we provide no method whereby the creator of a * domain over an array type could hide its ability to be subscripted.) */ - *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod); + *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod); /* * We treat int2vector and oidvector as though they were domains over @@ -233,25 +237,20 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) * xxxvector type; so we want the result of a slice operation to be * considered to be of the more general type. */ - if (*arrayType == INT2VECTOROID) - *arrayType = INT2ARRAYOID; - else if (*arrayType == OIDVECTOROID) - *arrayType = OIDARRAYOID; + if (*containerType == INT2VECTOROID) + *containerType = INT2ARRAYOID; + else if (*containerType == OIDVECTOROID) + *containerType = OIDARRAYOID; /* Get the type tuple for the array */ - type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType)); + type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType)); if (!HeapTupleIsValid(type_tuple_array)) - elog(ERROR, "cache lookup failed for type %u", *arrayType); + elog(ERROR, "cache lookup failed for type %u", *containerType); type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array); /* needn't check typisdefined since this will fail anyway */ elementType = type_struct_array->typelem; - if (elementType == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("cannot subscript type %s because it is not an array", - format_type_be(origArrayType)))); ReleaseSysCache(type_tuple_array); @@ -259,61 +258,80 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) } /* - * transformArraySubscripts() - * Transform array subscripting. This is used for both - * array fetch and array assignment. + * transformContainerSubscripts() + * Transform container subscripting. This is used for both + * container fetch and container assignment. * - * In an array fetch, we are given a source array value and we produce an - * expression that represents the result of extracting a single array element - * or an array slice. + * In a container fetch, we are given a source container value and we produce + * an expression that represents the result of extracting a single container + * element or a container slice. * - * In an array assignment, we are given a destination array value plus a - * source value that is to be assigned to a single element or a slice of - * that array. We produce an expression that represents the new array value - * with the source data inserted into the right part of the array. + * In a container assignment, we are given a destination container value plus a + * source value that is to be assigned to a single element or a slice of that + * container. We produce an expression that represents the new container value + * with the source data inserted into the right part of the container. * - * For both cases, if the source array is of a domain-over-array type, - * the result is of the base array type or its element type; essentially, - * we must fold a domain to its base type before applying subscripting. - * (Note that int2vector and oidvector are treated as domains here.) + * For both cases, this function contains only general subscription logic while + * type-specific logic (e.g. type verifications and coersion) is placend in + * separate procedure indicated by typsubscription. There is only one exception + * for now about domain-over-container, if the source container is of a + * domain-over-container type, the result is of the base container type or its + * element type; essentially, we must fold a domain to its base type before + * applying subscripting. (Note that int2vector and oidvector are treated as + * domains here.) If domain verification failed we assume, that element type + * must be the same as container type (e.g. in case of jsonb). + * An error will appear in case if current container type doesn't have a + * subscription procedure. * - * pstate Parse state - * arrayBase Already-transformed expression for the array as a whole - * arrayType OID of array's datatype (should match type of arrayBase, - * or be the base type of arrayBase's domain type) - * elementType OID of array's element type (fetch with transformArrayType, - * or pass InvalidOid to do it here) - * arrayTypMod typmod for the array (which is also typmod for the elements) - * indirection Untransformed list of subscripts (must not be NIL) - * assignFrom NULL for array fetch, else transformed expression for source. + * pstate Parse state + * containerBase Already-transformed expression for the container as a whole + * containerType OID of container's datatype (should match type of containerBase, + * or be the base type of containerBase's domain type) + * elementType OID of container's element type (fetch with transformArrayType, + * or pass InvalidOid to do it here) + * containerTypMod typmod for the container (which is also typmod for the elements) + * indirection Untransformed list of subscripts (must not be NIL) + * assignFrom NULL for container fetch, else transformed expression for source. */ -ArrayRef * -transformArraySubscripts(ParseState *pstate, - Node *arrayBase, - Oid arrayType, - Oid elementType, - int32 arrayTypMod, - List *indirection, - Node *assignFrom) + +SubscriptionRef * +transformContainerSubscripts(ParseState *pstate, + Node *containerBase, + Oid containerType, + Oid elementType, + int32 containerTypMod, + List *indirection, + Node *assignFrom) { - bool isSlice = false; - List *upperIndexpr = NIL; - List *lowerIndexpr = NIL; - ListCell *idx; - ArrayRef *aref; + bool isSlice = false; + List *upperIndexpr = NIL; + List *lowerIndexpr = NIL; + ListCell *idx; + SubscriptionRef *sbsref, + *prepared_sbsref; + RegProcedure typsubscription = get_typsubscription(containerType); + + if (!OidIsValid(typsubscription)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("cannot subscript type %s because it does not support subscription", + format_type_be(containerType)))); /* * Caller may or may not have bothered to determine elementType. Note - * that if the caller did do so, arrayType/arrayTypMod must be as modified + * that if the caller did do so, containerType/containerTypMod must be as modified * by transformArrayType, ie, smash domain to base type. */ if (!OidIsValid(elementType)) - elementType = transformArrayType(&arrayType, &arrayTypMod); + elementType = transformArrayType(&containerType, &containerTypMod); + + if (!OidIsValid(elementType)) + elementType = containerType; /* - * A list containing only simple subscripts refers to a single array + * A list containing only simple subscripts refers to a single container * element. If any of the items are slice specifiers (lower:upper), then - * the subscript expression means an array slice operation. In this case, + * the subscript expression means an container slice operation. In this case, * we convert any non-slice items to slices by treating the single * subscript as the upper bound and supplying an assumed lower bound of 1. * We have to prescan the list to see if there are any slice items. @@ -343,107 +361,37 @@ transformArraySubscripts(ParseState *pstate, if (ai->lidx) { subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind); - /* If it's not int4 already, try to coerce */ - subexpr = coerce_to_target_type(pstate, - subexpr, exprType(subexpr), - INT4OID, -1, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (subexpr == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array subscript must have type integer"), - parser_errposition(pstate, exprLocation(ai->lidx)))); - } - else if (!ai->is_slice) - { - /* Make a constant 1 */ - subexpr = (Node *) makeConst(INT4OID, - -1, - InvalidOid, - sizeof(int32), - Int32GetDatum(1), - false, - true); /* pass by value */ } else { /* Slice with omitted lower bound, put NULL into the list */ subexpr = NULL; } - lowerIndexpr = lappend(lowerIndexpr, subexpr); - } - else - Assert(ai->lidx == NULL && !ai->is_slice); - - if (ai->uidx) - { - subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); - /* If it's not int4 already, try to coerce */ - subexpr = coerce_to_target_type(pstate, - subexpr, exprType(subexpr), - INT4OID, -1, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (subexpr == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array subscript must have type integer"), - parser_errposition(pstate, exprLocation(ai->uidx)))); - } - else - { - /* Slice with omitted upper bound, put NULL into the list */ - Assert(isSlice && ai->is_slice); - subexpr = NULL; + lowerIndexpr = lappend(lowerIndexpr, list_make2(subexpr, ai)); } + subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind); upperIndexpr = lappend(upperIndexpr, subexpr); } /* - * If doing an array store, coerce the source value to the right type. - * (This should agree with the coercion done by transformAssignedExpr.) - */ - if (assignFrom != NULL) - { - Oid typesource = exprType(assignFrom); - Oid typeneeded = isSlice ? arrayType : elementType; - Node *newFrom; - - newFrom = coerce_to_target_type(pstate, - assignFrom, typesource, - typeneeded, arrayTypMod, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); - if (newFrom == NULL) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array assignment requires type %s" - " but expression is of type %s", - format_type_be(typeneeded), - format_type_be(typesource)), - errhint("You will need to rewrite or cast the expression."), - parser_errposition(pstate, exprLocation(assignFrom)))); - assignFrom = newFrom; - } - - /* - * Ready to build the ArrayRef node. + * Ready to build the SubscriptionRef node. */ - aref = makeNode(ArrayRef); - aref->refarraytype = arrayType; - aref->refelemtype = elementType; - aref->reftypmod = arrayTypMod; + sbsref = makeNode(SubscriptionRef); + sbsref->refcontainertype = containerType; + sbsref->refelemtype = elementType; + sbsref->reftypmod = containerTypMod; /* refcollid will be set by parse_collate.c */ - aref->refupperindexpr = upperIndexpr; - aref->reflowerindexpr = lowerIndexpr; - aref->refexpr = (Expr *) arrayBase; - aref->refassgnexpr = (Expr *) assignFrom; + sbsref->refupperindexpr = upperIndexpr; + sbsref->reflowerindexpr = lowerIndexpr; + sbsref->refexpr = (Expr *) containerBase; + sbsref->refassgnexpr = (Expr *) assignFrom; + + prepared_sbsref = (SubscriptionRef *) OidFunctionCall3(typsubscription, + Int32GetDatum(SBS_VALIDATION), + PointerGetDatum(sbsref), + PointerGetDatum(pstate)); - return aref; + return prepared_sbsref; } /* diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index b7b82bf..026e463 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -759,41 +759,24 @@ transformAssignmentIndirection(ParseState *pstate, /* base case: just coerce RHS to match target type ID */ - result = coerce_to_target_type(pstate, - rhs, exprType(rhs), - targetTypeId, targetTypMod, - COERCION_ASSIGNMENT, - COERCE_IMPLICIT_CAST, - -1); + if (targetTypeId != InvalidOid) + result = coerce_to_target_type(pstate, + rhs, exprType(rhs), + targetTypeId, targetTypMod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + else + result = rhs; + if (result == NULL) - { - if (targetIsArray) - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("array assignment to \"%s\" requires type %s" - " but expression is of type %s", - targetName, - format_type_be(targetTypeId), - format_type_be(exprType(rhs))), - errhint("You will need to rewrite or cast the expression."), - parser_errposition(pstate, location))); - else - ereport(ERROR, - (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("subfield \"%s\" is of type %s" - " but expression is of type %s", - targetName, - format_type_be(targetTypeId), - format_type_be(exprType(rhs))), - errhint("You will need to rewrite or cast the expression."), - parser_errposition(pstate, location))); - } + result = rhs; return result; } /* - * helper for transformAssignmentIndirection: process array assignment + * helper for transformAssignmentIndirection: process container assignment */ static Node * transformAssignmentSubscripts(ParseState *pstate, @@ -809,55 +792,55 @@ transformAssignmentSubscripts(ParseState *pstate, int location) { Node *result; - Oid arrayType; - int32 arrayTypMod; + Oid containerType; + int32 containerTypMod; Oid elementTypeId; Oid typeNeeded; Oid collationNeeded; Assert(subscripts != NIL); - /* Identify the actual array type and element type involved */ - arrayType = targetTypeId; - arrayTypMod = targetTypMod; - elementTypeId = transformArrayType(&arrayType, &arrayTypMod); + /* Identify the actual container type and element type involved */ + containerType = targetTypeId; + containerTypMod = targetTypMod; + elementTypeId = transformArrayType(&containerType, &containerTypMod); /* Identify type that RHS must provide */ - typeNeeded = isSlice ? arrayType : elementTypeId; + typeNeeded = isSlice ? containerType : elementTypeId; /* - * Array normally has same collation as elements, but there's an - * exception: we might be subscripting a domain over an array type. In + * container normally has same collation as elements, but there's an + * exception: we might be subscripting a domain over an container type. In * that case use collation of the base type. */ - if (arrayType == targetTypeId) + if (containerType == targetTypeId) collationNeeded = targetCollation; else - collationNeeded = get_typcollation(arrayType); + collationNeeded = get_typcollation(containerType); - /* recurse to create appropriate RHS for array assign */ + /* recurse to create appropriate RHS for container assign */ rhs = transformAssignmentIndirection(pstate, NULL, targetName, true, typeNeeded, - arrayTypMod, + containerTypMod, collationNeeded, next_indirection, rhs, location); /* process subscripts */ - result = (Node *) transformArraySubscripts(pstate, - basenode, - arrayType, - elementTypeId, - arrayTypMod, - subscripts, - rhs); - - /* If target was a domain over array, need to coerce up to the domain */ - if (arrayType != targetTypeId) + result = (Node *) transformContainerSubscripts(pstate, + basenode, + containerType, + exprType(rhs), + containerTypMod, + subscripts, + rhs); + + /* If target was a domain over container, need to coerce up to the domain */ + if (containerType != targetTypeId) { Oid resulttype = exprType(result); diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index b828e3c..44b4c04 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -895,7 +895,7 @@ process_matched_tle(TargetEntry *src_tle, /*---------- * Multiple assignments to same attribute. Allow only if all are - * FieldStore or ArrayRef assignment operations. This is a bit + * FieldStore or SubscriptionRef assignment operations. This is a bit * tricky because what we may actually be looking at is a nest of * such nodes; consider * UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y @@ -903,7 +903,7 @@ process_matched_tle(TargetEntry *src_tle, * FieldStore(col, fld1, FieldStore(placeholder, subfld1, x)) * FieldStore(col, fld2, FieldStore(placeholder, subfld2, y)) * However, we can ignore the substructure and just consider the top - * FieldStore or ArrayRef from each assignment, because it works to + * FieldStore or SubscriptionRef from each assignment, because it works to * combine these as * FieldStore(FieldStore(col, fld1, * FieldStore(placeholder, subfld1, x)), @@ -913,7 +913,7 @@ process_matched_tle(TargetEntry *src_tle, * * For FieldStore, instead of nesting we can generate a single * FieldStore with multiple target fields. We must nest when - * ArrayRefs are involved though. + * SubscriptionRefs are involved though. *---------- */ src_expr = (Node *) src_tle->expr; @@ -972,13 +972,13 @@ process_matched_tle(TargetEntry *src_tle, } newexpr = (Node *) fstore; } - else if (IsA(src_expr, ArrayRef)) + else if (IsA(src_expr, SubscriptionRef)) { - ArrayRef *aref = makeNode(ArrayRef); + SubscriptionRef *sbsref = makeNode(SubscriptionRef); - memcpy(aref, src_expr, sizeof(ArrayRef)); - aref->refexpr = (Expr *) prior_expr; - newexpr = (Node *) aref; + memcpy(sbsref, src_expr, sizeof(SubscriptionRef)); + sbsref->refexpr = (Expr *) prior_expr; + newexpr = (Node *) sbsref; } else { @@ -1005,14 +1005,15 @@ get_assignment_input(Node *node) return (Node *) fstore->arg; } - else if (IsA(node, ArrayRef)) + else if (IsA(node, SubscriptionRef)) { - ArrayRef *aref = (ArrayRef *) node; + SubscriptionRef *sbsref = (SubscriptionRef *) node; - if (aref->refassgnexpr == NULL) + if (sbsref->refassgnexpr == NULL) return NULL; - return (Node *) aref->refexpr; + return (Node *) sbsref->refexpr; } + return NULL; } diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 1db7bf0..1894e70 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -24,6 +24,8 @@ #include "catalog/pg_type.h" #include "funcapi.h" #include "libpq/pqformat.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "utils/array.h" #include "utils/arrayaccess.h" #include "utils/builtins.h" @@ -31,6 +33,8 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/typcache.h" +#include "parser/parse_node.h" +#include "parser/parse_coerce.h" /* @@ -88,6 +92,7 @@ typedef struct ArrayIteratorData static bool array_isspace(char ch); static int ArrayCount(const char *str, int *dim, char typdelim); +static bool isAssignmentIndirectionExpr(ExprState *exprstate); static void ReadArrayStr(char *arrayStr, const char *origStr, int nitems, int ndim, int *dim, FmgrInfo *inputproc, Oid typioparam, int32 typmod, @@ -157,7 +162,8 @@ static int width_bucket_array_variable(Datum operand, ArrayType *thresholds, Oid collation, TypeCacheEntry *typentry); - +static Datum array_subscription_prepare(PG_FUNCTION_ARGS); +static Datum array_subscription_evaluate(PG_FUNCTION_ARGS); /* * array_in : @@ -6520,3 +6526,363 @@ width_bucket_array_variable(Datum operand, return left; } + +/* + * Helper for ExecEvalSubscriptionRef: is expr a nested FieldStore or SubscriptionRef + * that might need the old element value passed down? + * + * (We could use this in ExecEvalFieldStore too, but in that case passing + * the old value is so cheap there's no need.) + */ +static bool +isAssignmentIndirectionExpr(ExprState *exprstate) +{ + if (exprstate == NULL) + return false; /* just paranoia */ + if (IsA(exprstate, FieldStoreState)) + { + FieldStore *fstore = (FieldStore *) exprstate->expr; + + if (fstore->arg && IsA(fstore->arg, CaseTestExpr)) + return true; + } + else if (IsA(exprstate, SubscriptionRefExprState)) + { + SubscriptionRef *array_ref = (SubscriptionRef *) exprstate->expr; + + if (array_ref->refexpr && IsA(array_ref->refexpr, CaseTestExpr)) + return true; + } + + return false; +} + +/* + * Perform an actual data extraction or modification for the array + * subscription. As a result the extracted Datum or the modified containers + * value will be returned. + */ +Datum +array_subscription_evaluate(PG_FUNCTION_ARGS) +{ + SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0); + SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1); + ExprContext *econtext = sbsdata->xprcontext; + bool *is_null = sbsdata->isNull; + SubscriptionRef *array_ref = (SubscriptionRef *) sbstate->xprstate.expr; + bool is_assignment = (array_ref->refassgnexpr != NULL); + bool is_slice = (array_ref->reflowerindexpr != NIL); + IntArray u_index, l_index; + bool eisnull; + int i = 0; + + get_typlenbyvalalign(array_ref->refelemtype, + &sbstate->refelemlength, + &sbstate->refelembyval, + &sbstate->refelemalign); + + for(i = 0; i < sbsdata->indexprNumber; i++) + u_index.indx[i] = DatumGetInt32(sbsdata->upper[i]); + + if (is_slice) + { + for(i = 0; i < sbsdata->indexprNumber; i++) + l_index.indx[i] = DatumGetInt32(sbsdata->lower[i]); + } + + if (is_assignment) + { + Datum sourceData; + Datum save_datum; + bool save_isNull; + + /* + * We might have a nested-assignment situation, in which the + * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to + * obtain and modify the previous value of the array element or slice + * being replaced. If so, we have to extract that value from the + * array and pass it down via the econtext's caseValue. It's safe to + * reuse the CASE mechanism because there cannot be a CASE between + * here and where the value would be needed, and an array assignment + * can't be within a CASE either. (So saving and restoring the + * caseValue is just paranoia, but let's do it anyway.) + * + * Since fetching the old element might be a nontrivial expense, do it + * only if the argument appears to actually need it. + */ + save_datum = econtext->caseValue_datum; + save_isNull = econtext->caseValue_isNull; + + if (isAssignmentIndirectionExpr(sbstate->refassgnexpr)) + { + if (*is_null) + { + /* whole array is null, so any element or slice is too */ + econtext->caseValue_datum = (Datum) 0; + econtext->caseValue_isNull = true; + } + else if (!is_slice) + { + econtext->caseValue_datum = + array_get_element(sbsdata->containerSource, sbsdata->indexprNumber, + u_index.indx, + sbstate->refattrlength, + sbstate->refelemlength, + sbstate->refelembyval, + sbstate->refelemalign, + &econtext->caseValue_isNull); + } + else + { + econtext->caseValue_datum = + array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber, + u_index.indx, l_index.indx, + sbsdata->upperProvided, + sbsdata->lowerProvided, + sbstate->refattrlength, + sbstate->refelemlength, + sbstate->refelembyval, + sbstate->refelemalign); + econtext->caseValue_isNull = false; + } + } + else + { + /* argument shouldn't need caseValue, but for safety set it null */ + econtext->caseValue_datum = (Datum) 0; + econtext->caseValue_isNull = true; + } + + /* + * Evaluate the value to be assigned into the array. + */ + sourceData = ExecEvalExpr(sbstate->refassgnexpr, + econtext, + &eisnull, + NULL); + + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; + + /* + * For an assignment to a fixed-length array type, both the original + * array and the value to be assigned into it must be non-NULL, else + * we punt and return the original array. + */ + if (sbstate->refattrlength > 0) /* fixed-length array? */ + if (eisnull || *is_null) + return sbsdata->containerSource; + + /* + * For assignment to varlena arrays, we handle a NULL original array + * by substituting an empty (zero-dimensional) array; insertion of the + * new element will result in a singleton array value. It does not + * matter whether the new element is NULL. + */ + if (*is_null) + { + sbsdata->containerSource = PointerGetDatum(construct_empty_array(array_ref->refelemtype)); + *is_null = false; + } + + if (!is_slice) + return array_set_element(sbsdata->containerSource, sbsdata->indexprNumber, + u_index.indx, sourceData, eisnull, + sbstate->refattrlength, + sbstate->refelemlength, + sbstate->refelembyval, + sbstate->refelemalign); + else + return array_set_slice(sbsdata->containerSource, sbsdata->indexprNumber, + u_index.indx, l_index.indx, + sbsdata->upperProvided, + sbsdata->lowerProvided, + sourceData, eisnull, + sbstate->refattrlength, + sbstate->refelemlength, + sbstate->refelembyval, + sbstate->refelemalign); + } + + if (!is_slice) + return array_get_element(sbsdata->containerSource, sbsdata->indexprNumber, + u_index.indx, + sbstate->refattrlength, + sbstate->refelemlength, + sbstate->refelembyval, + sbstate->refelemalign, + is_null); + else + return array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber, + u_index.indx, l_index.indx, + sbsdata->upperProvided, + sbsdata->lowerProvided, + sbstate->refattrlength, + sbstate->refelemlength, + sbstate->refelembyval, + sbstate->refelemalign); +} + +/* + * Perform preparation for the array subscription, mostly type verification + * and coersion. This function produces an expression that represents the + * result of extracting a single container element/container slice or the new + * container value with the source data inserted into the right part of the + * container. + */ +Datum +array_subscription_prepare(PG_FUNCTION_ARGS) +{ + SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0); + ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1); + Node *node = (Node *)sbsref; + Oid array_type = sbsref->refcontainertype; + int32 array_typ_mode = (int32) sbsref->reftypmod; + bool is_slice = sbsref->reflowerindexpr != NIL; + Oid typeneeded = InvalidOid, + typesource = InvalidOid; + Node *new_from; + Oid element_type_id; + Node *subexpr; + List *upperIndexpr = NIL; + List *lowerIndexpr = NIL; + ListCell *l; + + element_type_id = transformArrayType(&array_type, &array_typ_mode); + sbsref->refelemtype = element_type_id; + + foreach(l, sbsref->refupperindexpr) + { + subexpr = (Node *) lfirst(l); + + if (subexpr == NULL) + { + upperIndexpr = lappend(upperIndexpr, subexpr); + continue; + } + + subexpr = coerce_to_target_type(pstate, + subexpr, exprType(subexpr), + INT4OID, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + if (subexpr == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("array subscript must have type integer"), + parser_errposition(pstate, exprLocation(subexpr)))); + + upperIndexpr = lappend(upperIndexpr, subexpr); + } + + sbsref->refupperindexpr = upperIndexpr; + + foreach(l, sbsref->reflowerindexpr) + { + List *expr_ai = (List *) lfirst(l); + A_Indices *ai = (A_Indices *) lfirst(list_tail(expr_ai)); + + subexpr = (Node *) lfirst(list_head(expr_ai)); + if (subexpr == NULL && !ai->is_slice) + { + /* Make a constant 1 */ + subexpr = (Node *) makeConst(INT4OID, + -1, + InvalidOid, + sizeof(int32), + Int32GetDatum(1), + false, + true); /* pass by value */ + } + + if (subexpr == NULL) + { + lowerIndexpr = lappend(lowerIndexpr, subexpr); + continue; + } + + + subexpr = coerce_to_target_type(pstate, + subexpr, exprType(subexpr), + INT4OID, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + if (subexpr == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("array subscript must have type integer"), + parser_errposition(pstate, exprLocation(subexpr)))); + + lowerIndexpr = lappend(lowerIndexpr, subexpr); + } + + sbsref->reflowerindexpr = lowerIndexpr; + + if (sbsref->refassgnexpr != NULL) + { + new_from = coerce_to_target_type(pstate, + (Node *)sbsref->refassgnexpr, typesource, + typeneeded, sbsref->reftypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + if (new_from == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("array assignment requires type %s" + " but expression is of type %s", + format_type_be(typeneeded), + format_type_be(typesource)), + errhint("You will need to rewrite or cast the expression."), + parser_errposition(pstate, exprLocation((Node *)sbsref->refassgnexpr)))); + sbsref->refassgnexpr = (Expr *)new_from; + + if (array_type != sbsref->refcontainertype) + { + typesource = exprType((Node *)sbsref->refassgnexpr); + typesource = is_slice ? sbsref->refcontainertype : sbsref->refelemtype; + + node = coerce_to_target_type(pstate, + node, array_type, + sbsref->refcontainertype, sbsref->reftypmod, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + + /* can fail if we had int2vector/oidvector, but not for true domains */ + if (node == NULL && node->type != 0) + ereport(ERROR, + (errcode(ERRCODE_CANNOT_COERCE), + errmsg("cannot cast type %s to %s", + format_type_be(array_type), + format_type_be(sbsref->refcontainertype)), + parser_errposition(pstate, 0))); + + PG_RETURN_POINTER(node); + } + + } + + PG_RETURN_POINTER(sbsref); +} + +/* + * Handle array-type subscription logic. + */ +Datum +array_subscription(PG_FUNCTION_ARGS) +{ + int op_type = PG_GETARG_INT32(0); + FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1, + fcinfo->nargs); + + if (op_type & SBS_VALIDATION) + return array_subscription_prepare(&target_fcinfo); + + if (op_type & SBS_EXEC) + return array_subscription_evaluate(&target_fcinfo); + + elog(ERROR, "incorrect op_type for subscription function: %d", op_type); +} diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c index 987cfd1..27a5e89 100644 --- a/src/backend/utils/adt/jsonb.c +++ b/src/backend/utils/adt/jsonb.c @@ -1145,23 +1145,34 @@ to_jsonb(PG_FUNCTION_ARGS) { Datum val = PG_GETARG_DATUM(0); Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0); - JsonbInState result; - JsonbTypeCategory tcategory; - Oid outfuncoid; + JsonbValue *res = to_jsonb_worker(val, val_type); + PG_RETURN_POINTER(JsonbValueToJsonb(res)); +} - if (val_type == InvalidOid) +/* + * Do the actual conversion to jsonb for to_jsonb function. This logic is + * separated because it can be useful not only in here (e.g. we use it in + * jsonb subscription) + */ +JsonbValue * +to_jsonb_worker(Datum source, Oid source_type) +{ + JsonbInState result; + JsonbTypeCategory tcategory; + Oid outfuncoid; + + if (source_type == InvalidOid) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("could not determine input data type"))); - jsonb_categorize_type(val_type, + jsonb_categorize_type(source_type, &tcategory, &outfuncoid); memset(&result, 0, sizeof(JsonbInState)); - datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false); - - PG_RETURN_POINTER(JsonbValueToJsonb(result.res)); + datum_to_jsonb(source, false, &result, tcategory, outfuncoid, false); + return result.res; } /* diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c index ddc34ce..f192f72 100644 --- a/src/backend/utils/adt/jsonb_util.c +++ b/src/backend/utils/adt/jsonb_util.c @@ -61,6 +61,19 @@ static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate, JsonbIteratorToken seq, JsonbValue *scalarVal); +JsonbValue * +JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val) +{ + if (!val) + val = (JsonbValue *) palloc(sizeof(JsonbValue)); + + val->type = jbvBinary; + val->val.binary.data = &jsonb->root; + val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ; + + return val; +} + /* * Turn an in-memory JsonbValue into a Jsonb for on-disk storage. * @@ -520,6 +533,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq, JsonbValue *res = NULL; JsonbValue v; JsonbIteratorToken tok; + int i; + + if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject) + { + pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL); + for (i = 0; i < jbval->val.object.nPairs; i++) + { + pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key); + pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value); + } + + return pushJsonbValue(pstate, WJB_END_OBJECT, NULL); + } + + if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray) + { + pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL); + for (i = 0; i < jbval->val.array.nElems; i++) + { + pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]); + } + + return pushJsonbValue(pstate, WJB_END_ARRAY, NULL); + } if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) || jbval->type != jbvBinary) @@ -530,9 +567,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq, /* unpack the binary and add each piece to the pstate */ it = JsonbIteratorInit(jbval->val.binary.data); + + if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate) + { + tok = JsonbIteratorNext(&it, &v, true); + Assert(tok == WJB_BEGIN_ARRAY); + Assert(v.type == jbvArray && v.val.array.rawScalar); + + tok = JsonbIteratorNext(&it, &v, true); + Assert(tok == WJB_ELEM); + + res = pushJsonbValueScalar(pstate, seq, &v); + + tok = JsonbIteratorNext(&it, &v, true); + Assert(tok == WJB_END_ARRAY); + Assert(it == NULL); + + return res; + } + while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) res = pushJsonbValueScalar(pstate, tok, - tok < WJB_BEGIN_ARRAY ? &v : NULL); + tok < WJB_BEGIN_ARRAY || + (tok == WJB_BEGIN_ARRAY && + v.val.array.rawScalar) ? &v : NULL); return res; } diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 996007d..177c1ad 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -23,6 +23,8 @@ #include "lib/stringinfo.h" #include "mb/pg_wchar.h" #include "miscadmin.h" +#include "nodes/nodeFuncs.h" +#include "parser/parse_coerce.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/hsearch.h" @@ -32,6 +34,7 @@ #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/typcache.h" +#include "parser/parse_node.h" /* Operations available for setPath */ #define JB_PATH_NOOP 0x0000 @@ -136,18 +139,21 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container, /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */ static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2, JsonbParseState **state); +static Datum jsonb_set_element(Datum datum, Datum *path, int path_len, + Datum sourceData, Oid source_type); +static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath, + bool *isnull, bool as_text); static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, - JsonbParseState **st, int level, Jsonb *newval, - int op_type); + JsonbParseState **st, int level, JsonbValue *newval, int op_type); static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, - int level, - Jsonb *newval, uint32 npairs, int op_type); + int level, JsonbValue *newval, uint32 npairs, int op_type); static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, - int level, Jsonb *newval, uint32 nelems, int op_type); -static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb); + int level, JsonbValue *newval, uint32 nelems, int op_type); +static Datum jsonb_subscription_evaluate(PG_FUNCTION_ARGS); +static Datum jsonb_subscription_prepare(PG_FUNCTION_ARGS); /* state for json_object_keys */ typedef struct OkeysState @@ -1171,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) { Jsonb *jb = PG_GETARG_JSONB(0); ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); - Jsonb *res; Datum *pathtext; bool *pathnulls; + bool isnull; int npath; - int i; - bool have_object = false, - have_array = false; - JsonbValue *jbvp = NULL; - JsonbValue tv; - JsonbContainer *container; + Datum res; /* * If the array contains any null elements, return NULL, on the grounds @@ -1195,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) deconstruct_array(path, TEXTOID, -1, false, 'i', &pathtext, &pathnulls, &npath); - /* Identify whether we have object, array, or scalar at top-level */ - container = &jb->root; + res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text); + + if (isnull) + PG_RETURN_NULL(); + else + PG_RETURN_DATUM(res); +} +static Datum +jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text) +{ + Jsonb *res; + JsonbContainer *container = &jb->root; + JsonbValue *jbvp = NULL; + JsonbValue tv; + int i; + bool have_object = false, + have_array = false; + + *isnull = false; + + /* Identify whether we have object, array, or scalar at top-level */ if (JB_ROOT_IS_OBJECT(jb)) have_object = true; else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb)) @@ -1222,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) { if (as_text) { - PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL, + return PointerGetDatum(cstring_to_text(JsonbToCString(NULL, container, VARSIZE(jb)))); } else { /* not text mode - just hand back the jsonb */ - PG_RETURN_JSONB(jb); + return JsonbGetDatum(jb); } } @@ -1239,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) { jbvp = findJsonbValueFromContainerLen(container, JB_FOBJECT, - VARDATA_ANY(pathtext[i]), - VARSIZE_ANY_EXHDR(pathtext[i])); + VARDATA_ANY(path[i]), + VARSIZE_ANY_EXHDR(path[i])); } else if (have_array) { long lindex; uint32 index; - char *indextext = TextDatumGetCString(pathtext[i]); + char *indextext = TextDatumGetCString(path[i]); char *endptr; errno = 0; lindex = strtol(indextext, &endptr, 10); if (endptr == indextext || *endptr != '\0' || errno != 0 || lindex > INT_MAX || lindex < INT_MIN) - PG_RETURN_NULL(); + { + *isnull = true; + return PointerGetDatum(NULL); + } if (lindex >= 0) { @@ -1271,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) nelements = container->header & JB_CMASK; if (-lindex > nelements) - PG_RETURN_NULL(); + { + *isnull = true; + return PointerGetDatum(NULL); + } else index = nelements + lindex; } @@ -1281,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) else { /* scalar, extraction yields a null */ - PG_RETURN_NULL(); + *isnull = true; + return PointerGetDatum(NULL); } if (jbvp == NULL) - PG_RETURN_NULL(); + { + *isnull = true; + return PointerGetDatum(NULL); + } else if (i == npath - 1) break; @@ -1310,27 +1340,57 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text) { /* special-case outputs for string and null values */ if (jbvp->type == jbvString) - PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val, - jbvp->val.string.len)); + return PointerGetDatum( + cstring_to_text_with_len(jbvp->val.string.val, + jbvp->val.string.len)); if (jbvp->type == jbvNull) - PG_RETURN_NULL(); + { + *isnull = true; + return PointerGetDatum(NULL); + } } res = JsonbValueToJsonb(jbvp); if (as_text) { - PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL, + return PointerGetDatum(cstring_to_text(JsonbToCString(NULL, &res->root, VARSIZE(res)))); } else { /* not text mode - just hand back the jsonb */ - PG_RETURN_JSONB(res); + return JsonbGetDatum(res); } } +Datum +jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len, + Datum sourceData, Oid source_type) +{ + Jsonb *jb = DatumGetJsonb(jsonbdatum); + JsonbValue *newval, + *res; + JsonbParseState *state = NULL; + JsonbIterator *it; + bool *path_nulls = palloc0(path_len * sizeof(bool)); + + newval = to_jsonb_worker(sourceData, source_type); + + if (newval->type == jbvArray && newval->val.array.rawScalar) + *newval = newval->val.array.elems[0]; + + it = JsonbIteratorInit(&jb->root); + + res = setPath(&it, path, path_nulls, path_len, &state, 0, + newval, JB_PATH_CREATE); + + pfree(path_nulls); + + PG_RETURN_JSONB(JsonbValueToJsonb(res)); +} + /* * SQL function json_array_length(json) -> int */ @@ -3278,57 +3338,6 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS) } /* - * Add values from the jsonb to the parse state. - * - * If the parse state container is an object, the jsonb is pushed as - * a value, not a key. - * - * This needs to be done using an iterator because pushJsonbValue doesn't - * like getting jbvBinary values, so we can't just push jb as a whole. - */ -static void -addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb) -{ - JsonbIterator *it; - JsonbValue *o = &(*jbps)->contVal; - JsonbValue v; - JsonbIteratorToken type; - - it = JsonbIteratorInit(&jb->root); - - Assert(o->type == jbvArray || o->type == jbvObject); - - if (JB_ROOT_IS_SCALAR(jb)) - { - (void) JsonbIteratorNext(&it, &v, false); /* skip array header */ - (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */ - - switch (o->type) - { - case jbvArray: - (void) pushJsonbValue(jbps, WJB_ELEM, &v); - break; - case jbvObject: - (void) pushJsonbValue(jbps, WJB_VALUE, &v); - break; - default: - elog(ERROR, "unexpected parent of nested structure"); - } - } - else - { - while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE) - { - if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM) - (void) pushJsonbValue(jbps, type, &v); - else - (void) pushJsonbValue(jbps, type, NULL); - } - } - -} - -/* * SQL function jsonb_pretty (jsonb) * * Pretty-printed text for the jsonb @@ -3514,7 +3523,9 @@ jsonb_set(PG_FUNCTION_ARGS) { Jsonb *in = PG_GETARG_JSONB(0); ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); - Jsonb *newval = PG_GETARG_JSONB(2); + Jsonb *newjsonb = PG_GETARG_JSONB(2); + JsonbValue newvalbuf; + JsonbValue *newval = JsonbToJsonbValue(newjsonb, &newvalbuf); bool create = PG_GETARG_BOOL(3); JsonbValue *res = NULL; Datum *path_elems; @@ -3606,7 +3617,9 @@ jsonb_insert(PG_FUNCTION_ARGS) { Jsonb *in = PG_GETARG_JSONB(0); ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); - Jsonb *newval = PG_GETARG_JSONB(2); + Jsonb *newjsonb = PG_GETARG_JSONB(2); + JsonbValue newvalbuf; + JsonbValue *newval = JsonbToJsonbValue(newjsonb, &newvalbuf); bool after = PG_GETARG_BOOL(3); JsonbValue *res = NULL; Datum *path_elems; @@ -3766,10 +3779,10 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2, * All path elements before the last must already exist * whatever bits in op_type are set, or nothing is done. */ -static JsonbValue * +JsonbValue * setPath(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, - JsonbParseState **st, int level, Jsonb *newval, int op_type) + JsonbParseState **st, int level, JsonbValue *newval, int op_type) { JsonbValue v; JsonbIteratorToken r; @@ -3822,11 +3835,11 @@ setPath(JsonbIterator **it, Datum *path_elems, static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 npairs, int op_type) + JsonbValue *newval, uint32 npairs, int op_type) { - JsonbValue v; int i; - JsonbValue k; + JsonbValue k, + v; bool done = false; if (level >= path_len || path_nulls[level]) @@ -3843,7 +3856,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, newkey.val.string.val = VARDATA_ANY(path_elems[level]); (void) pushJsonbValue(st, WJB_KEY, &newkey); - addJsonbToParseState(st, newval); + (void) pushJsonbValue(st, WJB_VALUE, newval); } for (i = 0; i < npairs; i++) @@ -3874,7 +3887,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (!(op_type & JB_PATH_DELETE)) { (void) pushJsonbValue(st, WJB_KEY, &k); - addJsonbToParseState(st, newval); + (void) pushJsonbValue(st, WJB_VALUE, newval); } done = true; } @@ -3897,7 +3910,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, newkey.val.string.val = VARDATA_ANY(path_elems[level]); (void) pushJsonbValue(st, WJB_KEY, &newkey); - addJsonbToParseState(st, newval); + (void) pushJsonbValue(st, WJB_VALUE, newval); } (void) pushJsonbValue(st, r, &k); @@ -3929,9 +3942,9 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 nelems, int op_type) + JsonbValue *newval, uint32 nelems, int op_type) { - JsonbValue v; + JsonbValue v, *new = (JsonbValue *) newval; int idx, i; bool done = false; @@ -3976,8 +3989,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) && (op_type & JB_PATH_CREATE_OR_INSERT)) { - Assert(newval != NULL); - addJsonbToParseState(st, newval); + Assert(new != NULL); + (void) pushJsonbValue(st, WJB_ELEM, new); done = true; } @@ -3993,7 +4006,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, r = JsonbIteratorNext(it, &v, true); /* skip */ if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE)) - addJsonbToParseState(st, newval); + (void) pushJsonbValue(st, WJB_ELEM, new); /* * We should keep current value only in case of @@ -4004,13 +4017,13 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, (void) pushJsonbValue(st, r, &v); if (op_type & JB_PATH_INSERT_AFTER) - addJsonbToParseState(st, newval); + (void) pushJsonbValue(st, WJB_ELEM, new); done = true; } else (void) setPath(it, path_elems, path_nulls, path_len, - st, level + 1, newval, op_type); + st, level + 1, new, op_type); } else { @@ -4038,8 +4051,169 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, if (op_type & JB_PATH_CREATE_OR_INSERT && !done && level == path_len - 1 && i == nelems - 1) { - addJsonbToParseState(st, newval); + (void) pushJsonbValue(st, WJB_ELEM, new); } } } } + +/* + * Perform an actual data extraction or modification for the jsonb + * subscription. As a result the extracted Datum or the modified containers + * value will be returned. + */ +Datum +jsonb_subscription_evaluate(PG_FUNCTION_ARGS) +{ + SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0); + SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1); + SubscriptionRef *jsonb_ref = (SubscriptionRef *) sbstate->xprstate.expr; + bool *is_null = sbsdata->isNull; + bool is_assignment = (jsonb_ref->refassgnexpr != NULL); + + if (is_assignment) + { + ExprContext *econtext = sbsdata->xprcontext; + Datum sourceData; + Datum save_datum; + bool save_isNull; + bool eisnull; + + /* + * We might have a nested-assignment situation, in which the + * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to + * obtain and modify the previous value of the array element or slice + * being replaced. If so, we have to extract that value from the + * array and pass it down via the econtext's caseValue. It's safe to + * reuse the CASE mechanism because there cannot be a CASE between + * here and where the value would be needed, and an array assignment + * can't be within a CASE either. (So saving and restoring the + * caseValue is just paranoia, but let's do it anyway.) + * + * Since fetching the old element might be a nontrivial expense, do it + * only if the argument appears to actually need it. + */ + save_datum = econtext->caseValue_datum; + save_isNull = econtext->caseValue_isNull; + + /* + * Evaluate the value to be assigned into the array. + */ + sourceData = ExecEvalExpr(sbstate->refassgnexpr, + econtext, + &eisnull, + NULL); + + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; + + /* + * For an assignment to a fixed-length array type, both the original + * array and the value to be assigned into it must be non-NULL, else + * we punt and return the original array. + */ + if (sbstate->refattrlength > 0) /* fixed-length array? */ + if (eisnull || *is_null) + return sbsdata->containerSource; + + /* + * For assignment to varlena arrays, we handle a NULL original array + * by substituting an empty (zero-dimensional) array; insertion of the + * new element will result in a singleton array value. It does not + * matter whether the new element is NULL. + */ + if (*is_null) + { + sbsdata->containerSource = + PointerGetDatum(construct_empty_array(jsonb_ref->refelemtype)); + *is_null = false; + } + + return jsonb_set_element(sbsdata->containerSource, + sbsdata->upper, + sbsdata->indexprNumber, + sourceData, + jsonb_ref->refelemtype); + } + else + return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource), + sbsdata->upper, + sbsdata->indexprNumber, + is_null, + false); +} + +/* + * Perform preparation for the jsonb subscription. Since there are not any + * particular restrictions for this kind of subscription, we will verify that + * it is not a slice operation. This function produces an expression that + * represents the result of extracting a single container element or the new + * container value with the source data inserted into the right part of the + * container. + */ +Datum +jsonb_subscription_prepare(PG_FUNCTION_ARGS) +{ + SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0); + ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1); + List *upperIndexpr = NIL; + ListCell *l; + + if (sbsref->reflowerindexpr != NIL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("jsonb subscript does not support slices"), + parser_errposition(pstate, exprLocation( + ((Node *)lfirst(sbsref->reflowerindexpr->head)))))); + + foreach(l, sbsref->refupperindexpr) + { + Node *subexpr = (Node *) lfirst(l); + + Assert(subexpr != NULL); + + if (subexpr == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("jsonb subscript does not support slices"), + parser_errposition(pstate, exprLocation( + ((Node *) lfirst(sbsref->refupperindexpr->head)))))); + + subexpr = coerce_to_target_type(pstate, + subexpr, exprType(subexpr), + TEXTOID, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + if (subexpr == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("jsonb subscript must have text type"), + parser_errposition(pstate, exprLocation(subexpr)))); + + upperIndexpr = lappend(upperIndexpr, subexpr); + } + + sbsref->refupperindexpr = upperIndexpr; + + PG_RETURN_POINTER(sbsref); +} + +/* + * Handle jsonb-type subscription logic. + */ +Datum +jsonb_subscription(PG_FUNCTION_ARGS) +{ + int op_type = PG_GETARG_INT32(0); + FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1, + fcinfo->nargs); + + if (op_type & SBS_VALIDATION) + return jsonb_subscription_prepare(&target_fcinfo); + + if (op_type & SBS_EXEC) + return jsonb_subscription_evaluate(&target_fcinfo); + + elog(ERROR, "incorrect op_type for subscription function: %d", op_type); +} diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 8a81d7a..18dab1e 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -439,7 +439,7 @@ static void get_tablesample_def(TableSampleClause *tablesample, static void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); static Node *processIndirection(Node *node, deparse_context *context); -static void printSubscripts(ArrayRef *aref, deparse_context *context); +static void printSubscripts(SubscriptionRef *aref, deparse_context *context); static char *get_relation_name(Oid relid); static char *generate_relation_name(Oid relid, List *namespaces); static char *generate_qualified_relation_name(Oid relid); @@ -5828,7 +5828,7 @@ get_update_query_targetlist_def(Query *query, List *targetList, { /* * We must dig down into the expr to see if it's a PARAM_MULTIEXPR - * Param. That could be buried under FieldStores and ArrayRefs + * Param. That could be buried under FieldStores and SubscriptionRefs * (cf processIndirection()), and underneath those there could be * an implicit type coercion. */ @@ -5841,13 +5841,13 @@ get_update_query_targetlist_def(Query *query, List *targetList, expr = (Node *) linitial(fstore->newvals); } - else if (IsA(expr, ArrayRef)) + else if (IsA(expr, SubscriptionRef)) { - ArrayRef *aref = (ArrayRef *) expr; + SubscriptionRef *sbsref = (SubscriptionRef *) expr; - if (aref->refassgnexpr == NULL) + if (sbsref->refassgnexpr == NULL) break; - expr = (Node *) aref->refassgnexpr; + expr = (Node *) sbsref->refassgnexpr; } else break; @@ -6879,7 +6879,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) /* single words: always simple */ return true; - case T_ArrayRef: + case T_SubscriptionRef: case T_ArrayExpr: case T_RowExpr: case T_CoalesceExpr: @@ -6996,7 +6996,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) return true; /* own parentheses */ } case T_BoolExpr: /* lower precedence */ - case T_ArrayRef: /* other separators */ + case T_SubscriptionRef: /* other separators */ case T_ArrayExpr: /* other separators */ case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ @@ -7046,7 +7046,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags) return false; return true; /* own parentheses */ } - case T_ArrayRef: /* other separators */ + case T_SubscriptionRef: /* other separators */ case T_ArrayExpr: /* other separators */ case T_RowExpr: /* other separators */ case T_CoalesceExpr: /* own parentheses */ @@ -7232,9 +7232,9 @@ get_rule_expr(Node *node, deparse_context *context, get_windowfunc_expr((WindowFunc *) node, context); break; - case T_ArrayRef: + case T_SubscriptionRef: { - ArrayRef *aref = (ArrayRef *) node; + SubscriptionRef *sbsref = (SubscriptionRef *) node; bool need_parens; /* @@ -7245,24 +7245,24 @@ get_rule_expr(Node *node, deparse_context *context, * here too, and display only the assignment source * expression. */ - if (IsA(aref->refexpr, CaseTestExpr)) + if (IsA(sbsref->refexpr, CaseTestExpr)) { - Assert(aref->refassgnexpr); - get_rule_expr((Node *) aref->refassgnexpr, + Assert(sbsref->refassgnexpr); + get_rule_expr((Node *) sbsref->refassgnexpr, context, showimplicit); break; } /* * Parenthesize the argument unless it's a simple Var or a - * FieldSelect. (In particular, if it's another ArrayRef, we + * FieldSelect. (In particular, if it's another SubscriptionRef, we * *must* parenthesize to avoid confusion.) */ - need_parens = !IsA(aref->refexpr, Var) && - !IsA(aref->refexpr, FieldSelect); + need_parens = !IsA(sbsref->refexpr, Var) && + !IsA(sbsref->refexpr, FieldSelect); if (need_parens) appendStringInfoChar(buf, '('); - get_rule_expr((Node *) aref->refexpr, context, showimplicit); + get_rule_expr((Node *) sbsref->refexpr, context, showimplicit); if (need_parens) appendStringInfoChar(buf, ')'); @@ -7275,7 +7275,7 @@ get_rule_expr(Node *node, deparse_context *context, * EXPLAIN tries to print the targetlist of a plan resulting * from such a statement. */ - if (aref->refassgnexpr) + if (sbsref->refassgnexpr) { Node *refassgnexpr; @@ -7292,7 +7292,7 @@ get_rule_expr(Node *node, deparse_context *context, else { /* Just an ordinary array fetch, so print subscripts */ - printSubscripts(aref, context); + printSubscripts(sbsref, context); } } break; @@ -7491,12 +7491,12 @@ get_rule_expr(Node *node, deparse_context *context, bool need_parens; /* - * Parenthesize the argument unless it's an ArrayRef or + * Parenthesize the argument unless it's an SubscriptionRef or * another FieldSelect. Note in particular that it would be * WRONG to not parenthesize a Var argument; simplicity is not * the issue here, having the right number of names is. */ - need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect); + need_parens = !IsA(arg, SubscriptionRef) && !IsA(arg, FieldSelect); if (need_parens) appendStringInfoChar(buf, '('); get_rule_expr(arg, context, true); @@ -9610,7 +9610,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype, /* * processIndirection - take care of array and subfield assignment * - * We strip any top-level FieldStore or assignment ArrayRef nodes that + * We strip any top-level FieldStore or assignment SubscriptionRef nodes that * appear in the input, printing them as decoration for the base column * name (which we assume the caller just printed). Return the subexpression * that's to be assigned. @@ -9652,19 +9652,19 @@ processIndirection(Node *node, deparse_context *context) */ node = (Node *) linitial(fstore->newvals); } - else if (IsA(node, ArrayRef)) + else if (IsA(node, SubscriptionRef)) { - ArrayRef *aref = (ArrayRef *) node; + SubscriptionRef *sbsref = (SubscriptionRef *) node; - if (aref->refassgnexpr == NULL) + if (sbsref->refassgnexpr == NULL) break; - printSubscripts(aref, context); + printSubscripts(sbsref, context); /* * We ignore refexpr since it should be an uninteresting reference * to the target column or subcolumn. */ - node = (Node *) aref->refassgnexpr; + node = (Node *) sbsref->refassgnexpr; } else break; @@ -9674,14 +9674,14 @@ processIndirection(Node *node, deparse_context *context) } static void -printSubscripts(ArrayRef *aref, deparse_context *context) +printSubscripts(SubscriptionRef *sbsref, deparse_context *context) { StringInfo buf = context->buf; ListCell *lowlist_item; ListCell *uplist_item; - lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */ - foreach(uplist_item, aref->refupperindexpr) + lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */ + foreach(uplist_item, sbsref->refupperindexpr) { appendStringInfoChar(buf, '['); if (lowlist_item) diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 13ae6ad..ad8f334 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -3061,3 +3061,23 @@ get_range_subtype(Oid rangeOid) else return InvalidOid; } + +/* + * get_typsubscription + * + * Given the type OID, return the type's typsubscription procedure, if any. + */ +RegProcedure +get_typsubscription(Oid typid) +{ + HeapTuple tp; + RegProcedure result = InvalidOid; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (HeapTupleIsValid(tp)) + { + result = ((Form_pg_type) GETSTRUCT(tp))->typsubscription; + ReleaseSysCache(tp); + } + return result; +} diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 5d49fe5..837f033 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -1396,3 +1396,22 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases) return tupdesc; } + +FunctionCallInfoData +get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end) +{ + FunctionCallInfoData sliced_fcinfo; + int i; + + InitFunctionCallInfoData(sliced_fcinfo, fcinfo->flinfo, + fcinfo->nargs - 1, fcinfo->fncollation, + NULL, NULL); + + for(i = begin; i < end; i++) + { + sliced_fcinfo.arg[i - begin] = fcinfo->arg[i]; + sliced_fcinfo.argnull[i - begin] = false; + } + + return sliced_fcinfo; +} diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index e57b81c..23bf824 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -143,7 +143,7 @@ typedef FormData_pg_class *Form_pg_class; * Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId; * similarly, "1" in relminmxid stands for FirstMultiXactId */ -DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n 3 1 _null_ _null_ )); +DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n 3 1 _null_ _null_ )); DESCR(""); DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n 3 1 _null_ _null_ )); DESCR(""); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e2d08ba..4796d22 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5344,6 +5344,12 @@ DESCR("pg_controldata recovery state information as a function"); DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ )); DESCR("pg_controldata init state information as a function"); +/* type subscription support */ +DATA(insert OID = 3343 ( jsonb_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ jsonb_subscription _null_ _null_ _null_ )); +DESCR("Jsonb subscription logic"); +DATA(insert OID = 3344 ( array_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ array_subscription _null_ _null_ _null_ )); +DESCR("Array subscription logic"); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 162239c..b54d136 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -199,6 +199,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO */ Oid typcollation; + /* + * Type specific subscription logic. If typsubscription is none, it means + * that this type doesn't support subscription. + */ + regproc typsubscription; + #ifdef CATALOG_VARLEN /* variable-length fields start here */ /* @@ -236,7 +242,7 @@ typedef FormData_pg_type *Form_pg_type; * compiler constants for pg_type * ---------------- */ -#define Natts_pg_type 30 +#define Natts_pg_type 31 #define Anum_pg_type_typname 1 #define Anum_pg_type_typnamespace 2 #define Anum_pg_type_typowner 3 @@ -264,10 +270,10 @@ typedef FormData_pg_type *Form_pg_type; #define Anum_pg_type_typtypmod 25 #define Anum_pg_type_typndims 26 #define Anum_pg_type_typcollation 27 -#define Anum_pg_type_typdefaultbin 28 -#define Anum_pg_type_typdefault 29 -#define Anum_pg_type_typacl 30 - +#define Anum_pg_type_typsubscription 28 +#define Anum_pg_type_typdefaultbin 29 +#define Anum_pg_type_typdefault 30 +#define Anum_pg_type_typacl 31 /* ---------------- * initial contents of pg_type @@ -283,94 +289,94 @@ typedef FormData_pg_type *Form_pg_type; */ /* OIDS 1 - 99 */ -DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("boolean, 'true'/'false'"); #define BOOLOID 16 -DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("variable-length string, binary values escaped"); #define BYTEAOID 17 -DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("single character"); #define CHAROID 18 -DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("63-byte type for storing system identifiers"); #define NAMEOID 19 -DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("~18 digit integer, 8-byte storage"); #define INT8OID 20 -DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("-32 thousand to 32 thousand, 2-byte storage"); #define INT2OID 21 -DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("array of int2, used in system tables"); #define INT2VECTOROID 22 -DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("-2 billion to 2 billion integer, 4-byte storage"); #define INT4OID 23 -DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered procedure"); #define REGPROCOID 24 -DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ )); +DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 - _null_ _null_ _null_ )); DESCR("variable-length string, no limit specified"); #define TEXTOID 25 -DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("object identifier(oid), maximum 4 billion"); #define OIDOID 26 -DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("(block, offset), physical location of tuple"); #define TIDOID 27 -DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("transaction id"); #define XIDOID 28 -DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("command identifier type, sequence in transaction id"); #define CIDOID 29 -DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("array of oids, used in system tables"); #define OIDVECTOROID 30 /* hand-built rowtype entries for bootstrapped catalogs */ /* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */ -DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); +DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); +DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); +DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); /* OIDS 100 - 199 */ -DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ )); #define JSONOID 114 -DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("XML content"); #define XMLOID 142 -DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); -DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ )); +DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 - _null_ _null_ _null_ )); DESCR("string representing an internal node tree"); #define PGNODETREEOID 194 -DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("internal type for passing CollectedCommand"); #define PGDDLCOMMANDOID 32 /* OIDS 200 - 299 */ -DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("storage manager"); /* OIDS 300 - 399 */ @@ -380,276 +386,276 @@ DESCR("storage manager"); /* OIDS 500 - 599 */ /* OIDS 600 - 699 */ -DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("geometric point '(x, y)'"); #define POINTOID 600 -DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("geometric line segment '(pt1,pt2)'"); #define LSEGOID 601 -DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("geometric path '(pt1,...)'"); #define PATHOID 602 -DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("geometric box '(lower left,upper right)'"); #define BOXOID 603 -DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("geometric polygon '(pt1,...)'"); #define POLYGONOID 604 -DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); DESCR("geometric line"); #define LINEOID 628 -DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* OIDS 700 - 799 */ -DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("single-precision floating point number, 4-byte storage"); #define FLOAT4OID 700 -DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("double-precision floating point number, 8-byte storage"); #define FLOAT8OID 701 -DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("absolute, limited-range date and time (Unix system time)"); #define ABSTIMEOID 702 -DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("relative, limited-range time interval (Unix delta time)"); #define RELTIMEOID 703 -DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("(abstime,abstime), time interval"); #define TINTERVALOID 704 -DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR(""); #define UNKNOWNOID 705 -DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("geometric circle '(center,radius)'"); #define CIRCLEOID 718 -DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("monetary amounts, $d,ddd.cc"); #define CASHOID 790 -DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* OIDS 800 - 899 */ -DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("XX:XX:XX:XX:XX:XX, MAC address"); #define MACADDROID 829 -DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("IP address/netmask, host address, netmask optional"); #define INETOID 869 -DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("network IP address/netmask, network address"); #define CIDROID 650 /* OIDS 900 - 999 */ /* OIDS 1000 - 1099 */ -DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); #define INT2ARRAYOID 1005 -DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); #define INT4ARRAYOID 1007 -DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ )); +DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ )); #define TEXTARRAYOID 1009 -DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); #define OIDARRAYOID 1028 -DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ )); -DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ )); -DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); #define FLOAT4ARRAYOID 1021 -DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("access control list"); #define ACLITEMOID 1033 -DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); #define CSTRINGARRAYOID 1263 -DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ )); +DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ )); DESCR("char(length), blank-padded string, fixed storage length"); #define BPCHAROID 1042 -DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ )); +DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ )); DESCR("varchar(length), non-blank-padded string, variable storage length"); #define VARCHAROID 1043 -DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("date"); #define DATEOID 1082 -DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("time of day"); #define TIMEOID 1083 /* OIDS 1100 - 1199 */ -DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("date and time"); #define TIMESTAMPOID 1114 -DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("date and time with time zone"); #define TIMESTAMPTZOID 1184 -DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("@ , time interval"); #define INTERVALOID 1186 -DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* OIDS 1200 - 1299 */ -DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("time of day with time zone"); #define TIMETZOID 1266 -DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* OIDS 1500 - 1599 */ -DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("fixed-length bit string"); #define BITOID 1560 -DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("variable-length bit string"); #define VARBITOID 1562 -DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* OIDS 1600 - 1699 */ /* OIDS 1700 - 1799 */ -DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("numeric(precision, decimal), arbitrary precision number"); #define NUMERICOID 1700 -DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("reference to cursor (portal name)"); #define REFCURSOROID 1790 /* OIDS 2200 - 2299 */ -DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); -DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered procedure (with args)"); #define REGPROCEDUREOID 2202 -DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered operator"); #define REGOPEROID 2203 -DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered operator (with args)"); #define REGOPERATOROID 2204 -DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered class"); #define REGCLASSOID 2205 -DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered type"); #define REGTYPEOID 2206 -DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered role"); #define REGROLEOID 4096 -DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered namespace"); #define REGNAMESPACEOID 4089 -DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); #define REGTYPEARRAYOID 2211 -DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* uuid */ -DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("UUID datatype"); #define UUIDOID 2950 -DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* pg_lsn */ -DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("PostgreSQL LSN datatype"); #define LSNOID 3220 -DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* text search */ -DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("text representation for text search"); #define TSVECTOROID 3614 -DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("GiST index internal text representation for text search"); #define GTSVECTOROID 3642 -DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("query representation for text search"); #define TSQUERYOID 3615 -DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered text search configuration"); #define REGCONFIGOID 3734 -DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("registered text search dictionary"); #define REGDICTIONARYOID 3769 -DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* jsonb */ -DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 jsonb_subscription _null_ _null_ _null_ )); DESCR("Binary JSON"); #define JSONBOID 3802 -DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); -DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("txid snapshot"); -DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* range types */ -DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("range of integers"); #define INT4RANGEOID 3904 -DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("range of numerics"); -DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("range of timestamps without time zone"); -DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("range of timestamps with time zone"); -DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("range of dates"); -DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ )); -DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); +DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ )); DESCR("range of bigints"); -DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); /* * pseudo-types @@ -664,41 +670,41 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr * but there is now support for it in records and arrays. Perhaps we should * just treat it as a regular base type? */ -DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); #define RECORDOID 2249 -DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ )); #define RECORDARRAYOID 2287 -DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ )); #define CSTRINGOID 2275 -DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define ANYOID 2276 -DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); #define ANYARRAYOID 2277 -DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define VOIDOID 2278 -DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define TRIGGEROID 2279 -DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define EVTTRIGGEROID 3838 -DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define LANGUAGE_HANDLEROID 2280 -DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ )); #define INTERNALOID 2281 -DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define OPAQUEOID 2282 -DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define ANYELEMENTOID 2283 -DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define ANYNONARRAYOID 2776 -DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define ANYENUMOID 3500 -DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define FDW_HANDLEROID 3115 -DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define INDEX_AM_HANDLEROID 325 -DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ )); #define TSM_HANDLEROID 3310 -DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ )); +DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 - _null_ _null_ _null_ )); #define ANYRANGEOID 3831 diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h index d7bbfdb..947cad0 100644 --- a/src/include/catalog/pg_type_fn.h +++ b/src/include/catalog/pg_type_fn.h @@ -52,7 +52,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid, int32 typeMod, int32 typNDims, bool typeNotNull, - Oid typeCollation); + Oid typeCollation, + Oid subscriptionProcedure); extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, @@ -70,6 +71,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace, bool isImplicitArray, Oid baseType, Oid typeCollation, + Oid subscriptionProcedure, Node *defaultExpr, bool rebuild); diff --git a/src/include/funcapi.h b/src/include/funcapi.h index e73a824..760d45f 100644 --- a/src/include/funcapi.h +++ b/src/include/funcapi.h @@ -183,6 +183,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes, Datum proargmodes, Datum proargnames); extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple); +extern FunctionCallInfoData get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end); /*---------- diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 4fa3661..1f548ea 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -644,25 +644,46 @@ typedef struct WindowFuncExprState } WindowFuncExprState; /* ---------------- - * ArrayRefExprState node + * SubscriptionRefExprState node * * Note: array types can be fixed-length (typlen > 0), but only when the * element type is itself fixed-length. Otherwise they are varlena structures * and have typlen = -1. In any case, an array type is never pass-by-value. * ---------------- */ -typedef struct ArrayRefExprState +typedef struct SubscriptionRefExprState { ExprState xprstate; List *refupperindexpr; /* states for child nodes */ List *reflowerindexpr; ExprState *refexpr; ExprState *refassgnexpr; - int16 refattrlength; /* typlen of array type */ - int16 refelemlength; /* typlen of the array element type */ + int16 refattrlength; /* typlen of container type */ + int16 refelemlength; /* typlen of the container element type */ bool refelembyval; /* is the element type pass-by-value? */ char refelemalign; /* typalign of the element type */ -} ArrayRefExprState; +} SubscriptionRefExprState; + +/* --------------------------------- + * Subscription exec information + * + * It contains all information which is required to perform type-specific data + * extraction or modification. This information will be gathered in + * `ExecEvalSubscriptionRef` function and passed to `typsubscription` + * procedure. + * --------------------------------- + */ +typedef struct SubscriptionExecData +{ + ExprContext *xprcontext; /* econtext for subscription */ + bool *isNull; + Datum *upper; /* upper boundary for subscription */ + Datum *lower; /* lower boundary for subscription */ + bool *upperProvided; + bool *lowerProvided; + Datum containerSource; + int indexprNumber; +} SubscriptionExecData; /* ---------------- * FuncExprState node diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 88297bb..a80285b 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -140,7 +140,7 @@ typedef enum NodeTag T_Aggref, T_GroupingFunc, T_WindowFunc, - T_ArrayRef, + T_SubscriptionRef, T_FuncExpr, T_NamedArgExpr, T_OpExpr, @@ -194,7 +194,7 @@ typedef enum NodeTag T_AggrefExprState, T_GroupingFuncExprState, T_WindowFuncExprState, - T_ArrayRefExprState, + T_SubscriptionRefExprState, T_FuncExprState, T_ScalarArrayOpExprState, T_BoolExprState, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 65510b0..6b30b90 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -350,18 +350,18 @@ typedef struct WindowFunc } WindowFunc; /* ---------------- - * ArrayRef: describes an array subscripting operation - * - * An ArrayRef can describe fetching a single element from an array, - * fetching a subarray (array slice), storing a single element into - * an array, or storing a slice. The "store" cases work with an - * initial array value and a source value that is inserted into the - * appropriate part of the array; the result of the operation is an - * entire new modified array value. - * - * If reflowerindexpr = NIL, then we are fetching or storing a single array - * element at the subscripts given by refupperindexpr. Otherwise we are - * fetching or storing an array slice, that is a rectangular subarray + * SubscriptionRef: describes a subscripting operation over a container + * + * An SubscriptionRef can describe fetching a single element from a container, + * fetching a part of container (e.g. array slice), storing a single element into + * a container, or storing a slice. The "store" cases work with an + * initial container value and a source value that is inserted into the + * appropriate part of the container; the result of the operation is an + * entire new modified container value. + * + * If reflowerindexpr = NIL, then we are fetching or storing a single container + * element at the subscripts given by refupperindexpr. Otherwise we are + * fetching or storing a container slice, that is a rectangular subcontainer * with lower and upper bounds given by the index expressions. * reflowerindexpr must be the same length as refupperindexpr when it * is not NIL. @@ -373,27 +373,27 @@ typedef struct WindowFunc * element; but it is the array type when doing subarray fetch or either * type of store. * - * Note: for the cases where an array is returned, if refexpr yields a R/W - * expanded array, then the implementation is allowed to modify that object + * Note: for the cases where a container is returned, if refexpr yields a R/W + * expanded container, then the implementation is allowed to modify that object * in-place and return the same object.) * ---------------- */ -typedef struct ArrayRef +typedef struct SubscriptionRef { Expr xpr; - Oid refarraytype; /* type of the array proper */ - Oid refelemtype; /* type of the array elements */ - int32 reftypmod; /* typmod of the array (and elements too) */ - Oid refcollid; /* OID of collation, or InvalidOid if none */ - List *refupperindexpr;/* expressions that evaluate to upper array - * indexes */ - List *reflowerindexpr;/* expressions that evaluate to lower array - * indexes, or NIL for single array element */ - Expr *refexpr; /* the expression that evaluates to an array - * value */ - Expr *refassgnexpr; /* expression for the source value, or NULL if - * fetch */ -} ArrayRef; + Oid refcontainertype; /* type of the container proper */ + Oid refelemtype; /* type of the container elements */ + int32 reftypmod; /* typmod of the container (and elements too) */ + Oid refcollid; /* OID of collation, or InvalidOid if none */ + List *refupperindexpr; /* expressions that evaluate to upper container + * indexes */ + List *reflowerindexpr; /* expressions that evaluate to lower container + * indexes, or NIL for single container element */ + Expr *refexpr; /* the expression that evaluates to an container + * value */ + Expr *refassgnexpr; /* expression for the source value, or NULL if + * fetch */ +} SubscriptionRef; /* * CoercionContext - distinguishes the allowed set of type casts @@ -734,7 +734,7 @@ typedef struct FieldSelect * * FieldStore represents the operation of modifying one field in a tuple * value, yielding a new tuple value (the input is not touched!). Like - * the assign case of ArrayRef, this is used to implement UPDATE of a + * the assign case of SubscriptionRef, this is used to implement UPDATE of a * portion of a column. * * A single FieldStore can actually represent updates of several different diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 6633586..49418ae 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -224,12 +224,17 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate); extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location); -extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod); -extern ArrayRef *transformArraySubscripts(ParseState *pstate, - Node *arrayBase, - Oid arrayType, +extern Oid transformArrayType(Oid *containerType, int32 *containerTypmod); + +/* Type of a stage in case of type-specific subscription procedure */ +#define SBS_VALIDATION 0x0001 +#define SBS_EXEC 0x0002 + +extern SubscriptionRef *transformContainerSubscripts(ParseState *pstate, + Node *containerBase, + Oid containerType, Oid elementType, - int32 arrayTypMod, + int32 containerTypMod, List *indirection, Node *assignFrom); extern Const *make_const(ParseState *pstate, Value *value, int location); diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 6164f11..64dd619 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -502,4 +502,9 @@ extern Datum array_positions(PG_FUNCTION_ARGS); */ extern Datum array_typanalyze(PG_FUNCTION_ARGS); +/* + * prototypes for functions with array subscription logic + */ +extern Datum array_subscription(PG_FUNCTION_ARGS); + #endif /* ARRAY_H */ diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h index fa52afc..5bc3afe 100644 --- a/src/include/utils/jsonb.h +++ b/src/include/utils/jsonb.h @@ -352,6 +352,7 @@ extern Datum jsonb_typeof(PG_FUNCTION_ARGS); /* generator routines */ extern Datum to_jsonb(PG_FUNCTION_ARGS); +extern JsonbValue *to_jsonb_worker(Datum source, Oid source_type); extern Datum jsonb_build_object(PG_FUNCTION_ARGS); extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS); @@ -425,6 +426,7 @@ extern JsonbValue *pushJsonbValue(JsonbParseState **pstate, extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container); extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested); +extern JsonbValue *JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val); extern Jsonb *JsonbValueToJsonb(JsonbValue *val); extern bool JsonbDeepContains(JsonbIterator **val, JsonbIterator **mContained); @@ -437,4 +439,7 @@ extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in, int estimated_len); +/* Jsonb subscription logic */ +extern Datum jsonb_subscription(PG_FUNCTION_ARGS); + #endif /* __JSONB_H__ */ diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index dcb8980..474957d 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -159,6 +159,7 @@ extern void free_attstatsslot(Oid atttype, extern char *get_namespace_name(Oid nspid); extern char *get_namespace_name_or_temp(Oid nspid); extern Oid get_range_subtype(Oid rangeOid); +extern RegProcedure get_typsubscription(Oid typid); #define type_is_array(typid) (get_element_type(typid) != InvalidOid) /* type_is_array_domain accepts both plain arrays and domains over arrays */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 470cf93..692ffbe 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -4742,7 +4742,7 @@ exec_assign_value(PLpgSQL_execstate *estate, /* * Evaluate the subscripts, switch into left-to-right order. - * Like ExecEvalArrayRef(), complain if any subscript is null. + * Like ExecEvalSubscriptionRef(), complain if any subscript is null. */ for (i = 0; i < nsubscripts; i++) { @@ -4790,7 +4790,7 @@ exec_assign_value(PLpgSQL_execstate *estate, * fixed-length array types we skip the assignment. We can't * support assignment of a null entry into a fixed-length * array, either, so that's a no-op too. This is all ugly but - * corresponds to the current behavior of ExecEvalArrayRef(). + * corresponds to the current behavior of ExecEvalSubscriptionRef(). */ if (arrayelem->arraytyplen > 0 && /* fixed-length array? */ (oldarrayisnull || isNull)) @@ -6498,9 +6498,9 @@ exec_simple_check_node(Node *node) case T_Param: return TRUE; - case T_ArrayRef: + case T_SubscriptionRef: { - ArrayRef *expr = (ArrayRef *) node; + SubscriptionRef *expr = (SubscriptionRef *) node; if (!exec_simple_check_node((Node *) expr->refupperindexpr)) return FALSE; diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index a6d25de..0d6baad 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -3441,3 +3441,211 @@ HINT: Try using the function jsonb_set to replace key value. select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true); ERROR: cannot replace existing key HINT: Try using the function jsonb_set to replace key value. +-- jsonb subscript +select ('123'::jsonb)['a']; + jsonb +------- + +(1 row) + +select ('123'::jsonb)[0]; + jsonb +------- + +(1 row) + +select ('{"a": 1}'::jsonb)['a']; + jsonb +------- + 1 +(1 row) + +select ('{"a": 1}'::jsonb)[0]; + jsonb +------- + +(1 row) + +select ('{"a": 1}'::jsonb)['not_exist']; + jsonb +------- + +(1 row) + +select ('[1, "2", null]'::jsonb)['a']; + jsonb +------- + +(1 row) + +select ('[1, "2", null]'::jsonb)[0]; + jsonb +------- + 1 +(1 row) + +select ('[1, "2", null]'::jsonb)['1']; + jsonb +------- + "2" +(1 row) + +select ('[1, "2", null]'::jsonb)[1.0]; + jsonb +------- + +(1 row) + +select ('[1, "2", null]'::jsonb)[2]; + jsonb +------- + null +(1 row) + +select ('[1, "2", null]'::jsonb)[3]; + jsonb +------- + +(1 row) + +select ('[1, "2", null]'::jsonb)[-2]; + jsonb +------- + "2" +(1 row) + +select ('[1, "2", null]'::jsonb)[1]['a']; + jsonb +------- + +(1 row) + +select ('[1, "2", null]'::jsonb)[1][0]; + jsonb +------- + +(1 row) + +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b']; + jsonb +------- + "c" +(1 row) + +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']; + jsonb +----------- + [1, 2, 3] +(1 row) + +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1]; + jsonb +------- + 2 +(1 row) + +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a']; + jsonb +------- + +(1 row) + +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']; + jsonb +--------------- + {"a2": "aaa"} +(1 row) + +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']; + jsonb +------- + "aaa" +(1 row) + +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3']; + jsonb +------- + +(1 row) + +select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1']; + jsonb +----------------------- + ["aaa", "bbb", "ccc"] +(1 row) + +select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2]; + jsonb +------- + "ccc" +(1 row) + +create TEMP TABLE test_jsonb_subscript ( + id int, + test_json jsonb +); +insert into test_jsonb_subscript values +(1, '{}'), -- empty jsonb +(2, '{"key": "value"}'); -- jsonb with data +-- update empty jsonb +update test_jsonb_subscript set test_json['a'] = 1 where id = 1; +select * from test_jsonb_subscript; + id | test_json +----+------------------ + 2 | {"key": "value"} + 1 | {"a": 1} +(2 rows) + +-- update jsonb with some data +update test_jsonb_subscript set test_json['a'] = 1 where id = 2; +select * from test_jsonb_subscript; + id | test_json +----+-------------------------- + 1 | {"a": 1} + 2 | {"a": 1, "key": "value"} +(2 rows) + +-- replace jsonb +update test_jsonb_subscript set test_json['a'] = 'test'; +select * from test_jsonb_subscript; + id | test_json +----+------------------------------- + 1 | {"a": "test"} + 2 | {"a": "test", "key": "value"} +(2 rows) + +-- replace by object +update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb; +select * from test_jsonb_subscript; + id | test_json +----+--------------------------------- + 1 | {"a": {"b": 1}} + 2 | {"a": {"b": 1}, "key": "value"} +(2 rows) + +-- replace by array +update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb; +select * from test_jsonb_subscript; + id | test_json +----+---------------------------------- + 1 | {"a": [1, 2, 3]} + 2 | {"a": [1, 2, 3], "key": "value"} +(2 rows) + +-- use jsonb subscription in where clause +select * from test_jsonb_subscript where test_json['key'] = '"value"'; + id | test_json +----+---------------------------------- + 2 | {"a": [1, 2, 3], "key": "value"} +(1 row) + +select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"'; + id | test_json +----+----------- +(0 rows) + +select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"'; + id | test_json +----+----------- +(0 rows) + diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index b84bd70..3ec66ed 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -867,3 +867,63 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true); select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"'); select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true); + +-- jsonb subscript +select ('123'::jsonb)['a']; +select ('123'::jsonb)[0]; +select ('{"a": 1}'::jsonb)['a']; +select ('{"a": 1}'::jsonb)[0]; +select ('{"a": 1}'::jsonb)['not_exist']; +select ('[1, "2", null]'::jsonb)['a']; +select ('[1, "2", null]'::jsonb)[0]; +select ('[1, "2", null]'::jsonb)['1']; +select ('[1, "2", null]'::jsonb)[1.0]; +select ('[1, "2", null]'::jsonb)[2]; +select ('[1, "2", null]'::jsonb)[3]; +select ('[1, "2", null]'::jsonb)[-2]; +select ('[1, "2", null]'::jsonb)[1]['a']; +select ('[1, "2", null]'::jsonb)[1][0]; +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b']; +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']; +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1]; +select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a']; +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']; +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']; +select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3']; +select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1']; +select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2]; + +create TEMP TABLE test_jsonb_subscript ( + id int, + test_json jsonb +); + +insert into test_jsonb_subscript values +(1, '{}'), -- empty jsonb +(2, '{"key": "value"}'); -- jsonb with data + +-- update empty jsonb +update test_jsonb_subscript set test_json['a'] = 1 where id = 1; +select * from test_jsonb_subscript; + +-- update jsonb with some data +update test_jsonb_subscript set test_json['a'] = 1 where id = 2; +select * from test_jsonb_subscript; + +-- replace jsonb +update test_jsonb_subscript set test_json['a'] = 'test'; +select * from test_jsonb_subscript; + +-- replace by object +update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb; +select * from test_jsonb_subscript; + +-- replace by array +update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb; +select * from test_jsonb_subscript; + +-- use jsonb subscription in where clause +select * from test_jsonb_subscript where test_json['key'] = '"value"'; +select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"'; +select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"'; + diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile index 16dc390..cf8082a 100644 --- a/src/tutorial/Makefile +++ b/src/tutorial/Makefile @@ -13,8 +13,8 @@ # #------------------------------------------------------------------------- -MODULES = complex funcs -DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql +MODULES = complex funcs subscription +DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscription.sql ifdef NO_PGXS subdir = src/tutorial diff --git a/src/tutorial/subscription.c b/src/tutorial/subscription.c new file mode 100644 index 0000000..4abe3a7 --- /dev/null +++ b/src/tutorial/subscription.c @@ -0,0 +1,230 @@ +/* + * src/tutorial/subscription.c + * + ****************************************************************************** + This file contains routines that can be bound to a Postgres backend and + called by the backend in the process of processing queries. The calling + format for these routines is dictated by Postgres architecture. +******************************************************************************/ + +#include "postgres.h" + +#include "catalog/pg_type.h" +#include "executor/executor.h" +#include "nodes/execnodes.h" +#include "nodes/nodeFuncs.h" +#include "parser/parse_coerce.h" +#include "parser/parse_node.h" +#include "utils/array.h" +#include "fmgr.h" +#include "funcapi.h" + +PG_MODULE_MAGIC; + +typedef struct Custom +{ + int first; + int second; +} Custom; + + +/***************************************************************************** + * Input/Output functions + *****************************************************************************/ + +PG_FUNCTION_INFO_V1(custom_in); + +Datum +custom_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + int firstValue, + secondValue; + Custom *result; + + if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input syntax for complex: \"%s\"", + str))); + + + result = (Custom *) palloc(sizeof(Custom)); + result->first = firstValue; + result->second = secondValue; + PG_RETURN_POINTER(result); +} + +PG_FUNCTION_INFO_V1(custom_out); + +Datum +custom_out(PG_FUNCTION_ARGS) +{ + Custom *custom = (Custom *) PG_GETARG_POINTER(0); + char *result; + + result = psprintf("(%d, %d)", custom->first, custom->second); + PG_RETURN_CSTRING(result); +} + +/***************************************************************************** + * Custom subscription logic functions + *****************************************************************************/ + +Datum +custom_subscription_evaluate(PG_FUNCTION_ARGS) +{ + SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0); + SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1); + SubscriptionRef *custom_ref = (SubscriptionRef *) sbstate->xprstate.expr; + Custom *result = (Custom *) sbsdata->containerSource; + bool *is_null = sbsdata->isNull; + bool is_assignment = (custom_ref->refassgnexpr != NULL); + + int index; + + if (sbsdata->indexprNumber != 1) + ereport(ERROR, (errmsg("custom does not support nested subscription"))); + + index = DatumGetInt32(sbsdata->upper[0]); + + if (is_assignment) + { + ExprContext *econtext = sbsdata->xprcontext; + Datum sourceData; + Datum save_datum; + bool save_isNull; + bool eisnull; + + /* + * We might have a nested-assignment situation, in which the + * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to + * obtain and modify the previous value of the array element or slice + * being replaced. If so, we have to extract that value from the + * array and pass it down via the econtext's caseValue. It's safe to + * reuse the CASE mechanism because there cannot be a CASE between + * here and where the value would be needed, and an array assignment + * can't be within a CASE either. (So saving and restoring the + * caseValue is just paranoia, but let's do it anyway.) + * + * Since fetching the old element might be a nontrivial expense, do it + * only if the argument appears to actually need it. + */ + save_datum = econtext->caseValue_datum; + save_isNull = econtext->caseValue_isNull; + + /* + * Evaluate the value to be assigned into the container. + */ + sourceData = ExecEvalExpr(sbstate->refassgnexpr, + econtext, + &eisnull, + NULL); + + econtext->caseValue_datum = save_datum; + econtext->caseValue_isNull = save_isNull; + + /* + * For an assignment to a fixed-length array type, both the original + * array and the value to be assigned into it must be non-NULL, else + * we punt and return the original array. + */ + if (sbstate->refattrlength > 0) /* fixed-length container? */ + if (eisnull || *is_null) + return sbsdata->containerSource; + + /* + * For assignment to varlena container, we handle a NULL original array + * by substituting an empty (zero-dimensional) array; insertion of the + * new element will result in a singleton array value. It does not + * matter whether the new element is NULL. + */ + if (*is_null) + { + sbsdata->containerSource = + PointerGetDatum(construct_empty_array(custom_ref->refelemtype)); + *is_null = false; + } + + if (index == 1) + result->first = DatumGetInt32(sourceData); + else + result->second = DatumGetInt32(sourceData); + + PG_RETURN_POINTER(result); + } + else + { + if (index == 1) + PG_RETURN_INT32(result->first); + else + PG_RETURN_INT32(result->second); + } +} + +Datum +custom_subscription_prepare(PG_FUNCTION_ARGS) +{ + SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0); + ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1); + List *upperIndexpr = NIL; + ListCell *l; + + if (sbsref->reflowerindexpr != NIL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("custom subscript does not support slices"), + parser_errposition(pstate, exprLocation( + ((Node *)lfirst(sbsref->reflowerindexpr->head)))))); + + foreach(l, sbsref->refupperindexpr) + { + Node *subexpr = (Node *) lfirst(l); + + Assert(subexpr != NULL); + + if (subexpr == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("custom subscript does not support slices"), + parser_errposition(pstate, exprLocation( + ((Node *) lfirst(sbsref->refupperindexpr->head)))))); + + subexpr = coerce_to_target_type(pstate, + subexpr, exprType(subexpr), + INT4OID, -1, + COERCION_ASSIGNMENT, + COERCE_IMPLICIT_CAST, + -1); + if (subexpr == NULL) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("custom subscript must have int type"), + parser_errposition(pstate, exprLocation(subexpr)))); + + upperIndexpr = lappend(upperIndexpr, subexpr); + } + + sbsref->refupperindexpr = upperIndexpr; + sbsref->refelemtype = INT4OID; + + PG_RETURN_POINTER(sbsref); +} + +PG_FUNCTION_INFO_V1(custom_subscription); + +Datum +custom_subscription(PG_FUNCTION_ARGS) +{ + int op_type = PG_GETARG_INT32(0); + FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1, + fcinfo->nargs); + + if (op_type & SBS_VALIDATION) + return custom_subscription_prepare(&target_fcinfo); + + if (op_type & SBS_EXEC) + return custom_subscription_evaluate(&target_fcinfo); + + elog(ERROR, "incorrect op_type for subscription function: %d", op_type); +} diff --git a/src/tutorial/subscription.source b/src/tutorial/subscription.source new file mode 100644 index 0000000..7479fa7 --- /dev/null +++ b/src/tutorial/subscription.source @@ -0,0 +1,71 @@ +--------------------------------------------------------------------------- +-- +-- subscription.sql- +-- This file shows how to create a new subscription procedure for +-- user-defined type. +-- +-- +-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group +-- Portions Copyright (c) 1994, Regents of the University of California +-- +-- src/tutorial/subscription.source +-- +--------------------------------------------------------------------------- + +----------------------------- +-- Creating a new type: +-- We are going to create a new type called 'complex' which represents +-- complex numbers. +-- A user-defined type must have an input and an output function, and +-- optionally can have binary input and output functions. All of these +-- are usually user-defined C functions. +----------------------------- + +-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX +-- (we do not want to assume this is in the dynamic loader search path). +-- Look at $PWD/complex.c for the source. Note that we declare all of +-- them as STRICT, so we do not need to cope with NULL inputs in the +-- C code. We also mark them IMMUTABLE, since they always return the +-- same outputs given the same inputs. + +-- the input function 'complex_in' takes a null-terminated string (the +-- textual representation of the type) and turns it into the internal +-- (in memory) representation. You will get a message telling you 'complex' +-- does not exist yet but that's okay. + +CREATE FUNCTION custom_in(cstring) + RETURNS custom + AS '_OBJWD_/subscription' + LANGUAGE C IMMUTABLE STRICT; + +-- the output function 'complex_out' takes the internal representation and +-- converts it into the textual representation. + +CREATE FUNCTION custom_out(custom) + RETURNS cstring + AS '_OBJWD_/subscription' + LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION custom_subscription(internal) + RETURNS internal + AS '_OBJWD_/subscription' + LANGUAGE C IMMUTABLE STRICT; + +CREATE TYPE custom ( + internallength = 8, + input = custom_in, + output = custom_out, + subscription = custom_subscription +); + +-- we can use it in a table + +CREATE TABLE test_subscription ( + data custom, +); + +INSERT INTO test_subscription VALUES ('(1, 2)'); + +SELECT data[0] from test_subscription; + +UPDATE test_subscription SET data[1] = 3;