← Back to Overview

src/backend/utils/adt/ruleutils.c

Coverage: 145/150 lines (96.7%)
Total Lines
150
modified
Covered
145
96.7%
Uncovered
5
3.3%
Keyboard navigation
pg_get_triggerdef_worker() lines 916-1180
Modified Lines Coverage: 1/1 lines (100.0%)
LineHitsSourceCommit
916 - pg_get_triggerdef_worker(Oid trigid, bool pretty) -
917 - { -
918 - HeapTuple ht_trig; -
919 - Form_pg_trigger trigrec; -
920 - StringInfoData buf; -
921 - Relation tgrel; -
922 - ScanKeyData skey[1]; -
923 - SysScanDesc tgscan; -
924 - int findx = 0; -
925 - char *tgname; -
926 - char *tgoldtable; -
927 - char *tgnewtable; -
928 - Datum value; -
929 - bool isnull; -
930 - -
931 - /* -
932 - * Fetch the pg_trigger tuple by the Oid of the trigger -
933 - */ -
934 - tgrel = table_open(TriggerRelationId, AccessShareLock); -
935 - -
936 - ScanKeyInit(&skey[0], -
937 - Anum_pg_trigger_oid, -
938 - BTEqualStrategyNumber, F_OIDEQ, -
939 - ObjectIdGetDatum(trigid)); -
940 - -
941 - tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true, -
942 - NULL, 1, skey); -
943 - -
944 - ht_trig = systable_getnext(tgscan); -
945 - -
946 - if (!HeapTupleIsValid(ht_trig)) -
947 - { -
948 - systable_endscan(tgscan); -
949 - table_close(tgrel, AccessShareLock); -
950 - return NULL; -
951 - } -
952 - -
953 - trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig); -
954 - -
955 - /* -
956 - * Start the trigger definition. Note that the trigger's name should never -
957 - * be schema-qualified, but the trigger rel's name may be. -
958 - */ -
959 - initStringInfo(&buf); -
960 - -
961 - tgname = NameStr(trigrec->tgname); -
962 - appendStringInfo(&buf, "CREATE %sTRIGGER %s ", -
963 - OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "", -
964 - quote_identifier(tgname)); -
965 - -
966 - if (TRIGGER_FOR_BEFORE(trigrec->tgtype)) -
967 - appendStringInfoString(&buf, "BEFORE"); -
968 - else if (TRIGGER_FOR_AFTER(trigrec->tgtype)) -
969 - appendStringInfoString(&buf, "AFTER"); -
970 - else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype)) -
971 - appendStringInfoString(&buf, "INSTEAD OF"); -
972 - else -
973 - elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype); -
974 - -
975 - if (TRIGGER_FOR_INSERT(trigrec->tgtype)) -
976 - { -
977 - appendStringInfoString(&buf, " INSERT"); -
978 - findx++; -
979 - } -
980 - if (TRIGGER_FOR_DELETE(trigrec->tgtype)) -
981 - { -
982 - if (findx > 0) -
983 - appendStringInfoString(&buf, " OR DELETE"); -
984 - else -
985 - appendStringInfoString(&buf, " DELETE"); -
986 - findx++; -
987 - } -
988 - if (TRIGGER_FOR_UPDATE(trigrec->tgtype)) -
989 - { -
990 - if (findx > 0) -
991 - appendStringInfoString(&buf, " OR UPDATE"); -
992 - else -
993 - appendStringInfoString(&buf, " UPDATE"); -
994 - findx++; -
995 - /* tgattr is first var-width field, so OK to access directly */ -
996 - if (trigrec->tgattr.dim1 > 0) -
997 - { -
998 - int i; -
999 - -
1000 - appendStringInfoString(&buf, " OF "); -
1001 - for (i = 0; i < trigrec->tgattr.dim1; i++) -
1002 - { -
1003 - char *attname; -
1004 - -
1005 - if (i > 0) -
1006 - appendStringInfoString(&buf, ", "); -
1007 - attname = get_attname(trigrec->tgrelid, -
1008 - trigrec->tgattr.values[i], false); -
1009 - appendStringInfoString(&buf, quote_identifier(attname)); -
1010 - } -
1011 - } -
1012 - } -
1013 - if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype)) -
1014 - { -
1015 - if (findx > 0) -
1016 - appendStringInfoString(&buf, " OR TRUNCATE"); -
1017 - else -
1018 - appendStringInfoString(&buf, " TRUNCATE"); -
1019 - findx++; -
1020 - } -
1021 - -
1022 - /* -
1023 - * In non-pretty mode, always schema-qualify the target table name for -
1024 - * safety. In pretty mode, schema-qualify only if not visible. -
1025 - */ -
1026 - appendStringInfo(&buf, " ON %s ", -
1027 - pretty ? -
1028 - generate_relation_name(trigrec->tgrelid, NIL) : -
1029 - generate_qualified_relation_name(trigrec->tgrelid)); -
1030 - -
1031 - if (OidIsValid(trigrec->tgconstraint)) -
1032 - { -
1033 - if (OidIsValid(trigrec->tgconstrrelid)) -
1034 - appendStringInfo(&buf, "FROM %s ", -
1035 - generate_relation_name(trigrec->tgconstrrelid, NIL)); -
1036 - if (!trigrec->tgdeferrable) -
1037 - appendStringInfoString(&buf, "NOT "); -
1038 - appendStringInfoString(&buf, "DEFERRABLE INITIALLY "); -
1039 - if (trigrec->tginitdeferred) -
1040 - appendStringInfoString(&buf, "DEFERRED "); -
1041 - else -
1042 - appendStringInfoString(&buf, "IMMEDIATE "); -
1043 - } -
1044 - -
1045 - value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable, -
1046 - tgrel->rd_att, &isnull); -
1047 - if (!isnull) -
1048 - tgoldtable = NameStr(*DatumGetName(value)); -
1049 - else -
1050 - tgoldtable = NULL; -
1051 - value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable, -
1052 - tgrel->rd_att, &isnull); -
1053 - if (!isnull) -
1054 - tgnewtable = NameStr(*DatumGetName(value)); -
1055 - else -
1056 - tgnewtable = NULL; -
1057 - if (tgoldtable != NULL || tgnewtable != NULL) -
1058 - { -
1059 - appendStringInfoString(&buf, "REFERENCING "); -
1060 - if (tgoldtable != NULL) -
1061 - appendStringInfo(&buf, "OLD TABLE AS %s ", -
1062 - quote_identifier(tgoldtable)); -
1063 - if (tgnewtable != NULL) -
1064 - appendStringInfo(&buf, "NEW TABLE AS %s ", -
1065 - quote_identifier(tgnewtable)); -
1066 - } -
1067 - -
1068 - if (TRIGGER_FOR_ROW(trigrec->tgtype)) -
1069 - appendStringInfoString(&buf, "FOR EACH ROW "); -
1070 - else -
1071 - appendStringInfoString(&buf, "FOR EACH STATEMENT "); -
1072 - -
1073 - /* If the trigger has a WHEN qualification, add that */ -
1074 - value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual, -
1075 - tgrel->rd_att, &isnull); -
1076 - if (!isnull) -
1077 - { -
1078 - Node *qual; -
1079 - char relkind; -
1080 - deparse_context context; -
1081 - deparse_namespace dpns; -
1082 - RangeTblEntry *oldrte; -
1083 - RangeTblEntry *newrte; -
1084 - -
1085 - appendStringInfoString(&buf, "WHEN ("); -
1086 - -
1087 - qual = stringToNode(TextDatumGetCString(value)); -
1088 - -
1089 - relkind = get_rel_relkind(trigrec->tgrelid); -
1090 - -
1091 - /* Build minimal OLD and NEW RTEs for the rel */ -
1092 - oldrte = makeNode(RangeTblEntry); -
1093 - oldrte->rtekind = RTE_RELATION; -
1094 - oldrte->relid = trigrec->tgrelid; -
1095 - oldrte->relkind = relkind; -
1096 - oldrte->rellockmode = AccessShareLock; -
1097 - oldrte->alias = makeAlias("old", NIL); -
1098 - oldrte->eref = oldrte->alias; -
1099 - oldrte->lateral = false; -
1100 - oldrte->inh = false; -
1101 - oldrte->inFromCl = true; -
1102 - -
1103 - newrte = makeNode(RangeTblEntry); -
1104 - newrte->rtekind = RTE_RELATION; -
1105 - newrte->relid = trigrec->tgrelid; -
1106 - newrte->relkind = relkind; -
1107 - newrte->rellockmode = AccessShareLock; -
1108 - newrte->alias = makeAlias("new", NIL); -
1109 - newrte->eref = newrte->alias; -
1110 - newrte->lateral = false; -
1111 - newrte->inh = false; -
1112 - newrte->inFromCl = true; -
1113 - -
1114 - /* Build two-element rtable */ -
1115 - memset(&dpns, 0, sizeof(dpns)); -
1116 - dpns.rtable = list_make2(oldrte, newrte); -
1117 - dpns.subplans = NIL; -
1118 - dpns.ctes = NIL; -
1119 - dpns.appendrels = NULL; -
1120 - set_rtable_names(&dpns, NIL, NULL); -
1121 - set_simple_column_names(&dpns); -
1122 - -
1123 - /* Set up context with one-deep namespace stack */ -
1124 - context.buf = &buf; -
1125 - context.namespaces = list_make1(&dpns); -
1126 - context.resultDesc = NULL; -
1127 - context.targetList = NIL; -
1128 - context.windowClause = NIL; -
1129 - context.varprefix = true; -
1130 - context.prettyFlags = GET_PRETTY_FLAGS(pretty); -
1131 - context.wrapColumn = WRAP_COLUMN_DEFAULT; -
1132 - context.indentLevel = PRETTYINDENT_STD; -
1133 - context.colNamesVisible = true; -
1134 - context.inGroupBy = false; -
1135 88 context.inRPRDefine = false; 7f135d9Recognize row pattern navigation operations by name in DEFINE
1136 - context.varInOrderBy = false; -
1137 - context.appendparents = NULL; -
1138 - -
1139 - get_rule_expr(qual, &context, false); -
1140 - -
1141 - appendStringInfoString(&buf, ") "); -
1142 - } -
1143 - -
1144 - appendStringInfo(&buf, "EXECUTE FUNCTION %s(", -
1145 - generate_function_name(trigrec->tgfoid, 0, -
1146 - NIL, NULL, -
1147 - false, NULL, false, false)); 7f135d9Recognize row pattern navigation operations by name in DEFINE
1148 - -
1149 - if (trigrec->tgnargs > 0) -
1150 - { -
1151 - char *p; -
1152 - int i; -
1153 - -
1154 - value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs, -
1155 - tgrel->rd_att, &isnull); -
1156 - if (isnull) -
1157 - elog(ERROR, "tgargs is null for trigger %u", trigid); -
1158 - p = (char *) VARDATA_ANY(DatumGetByteaPP(value)); -
1159 - for (i = 0; i < trigrec->tgnargs; i++) -
1160 - { -
1161 - if (i > 0) -
1162 - appendStringInfoString(&buf, ", "); -
1163 - simple_quote_literal(&buf, p); -
1164 - /* advance p to next string embedded in tgargs */ -
1165 - while (*p) -
1166 - p++; -
1167 - p++; -
1168 - } -
1169 - } -
1170 - -
1171 - /* We deliberately do not put semi-colon at end */ -
1172 - appendStringInfoChar(&buf, ')'); -
1173 - -
1174 - /* Clean up */ -
1175 - systable_endscan(tgscan); -
1176 - -
1177 - table_close(tgrel, AccessShareLock); -
1178 - -
1179 - return buf.data; -
1180 - } -
pg_get_functiondef() lines 3291-3538
Modified Lines Coverage: 0/0 lines (0.0%)
LineHitsSourceCommit
3291 - pg_get_functiondef(PG_FUNCTION_ARGS) -
3292 - { -
3293 - Oid funcid = PG_GETARG_OID(0); -
3294 - StringInfoData buf; -
3295 - StringInfoData dq; -
3296 - HeapTuple proctup; -
3297 - Form_pg_proc proc; -
3298 - bool isfunction; -
3299 - Datum tmp; -
3300 - bool isnull; -
3301 - const char *prosrc; -
3302 - const char *name; -
3303 - const char *nsp; -
3304 - float4 procost; -
3305 - int oldlen; -
3306 - -
3307 - initStringInfo(&buf); -
3308 - -
3309 - /* Look up the function */ -
3310 - proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); -
3311 - if (!HeapTupleIsValid(proctup)) -
3312 - PG_RETURN_NULL(); -
3313 - -
3314 - proc = (Form_pg_proc) GETSTRUCT(proctup); -
3315 - name = NameStr(proc->proname); -
3316 - -
3317 - if (proc->prokind == PROKIND_AGGREGATE) -
3318 - ereport(ERROR, -
3319 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
3320 - errmsg("\"%s\" is an aggregate function", name))); -
3321 - -
3322 - isfunction = (proc->prokind != PROKIND_PROCEDURE); -
3323 - -
3324 - /* -
3325 - * We always qualify the function name, to ensure the right function gets -
3326 - * replaced. -
3327 - */ -
3328 - nsp = get_namespace_name_or_temp(proc->pronamespace); -
3329 - appendStringInfo(&buf, "CREATE OR REPLACE %s %s(", -
3330 - isfunction ? "FUNCTION" : "PROCEDURE", -
3331 - quote_qualified_identifier(nsp, name)); -
3332 - (void) print_function_arguments(&buf, proctup, false, true); -
3333 - appendStringInfoString(&buf, ")\n"); -
3334 - if (isfunction) -
3335 - { -
3336 - appendStringInfoString(&buf, " RETURNS "); -
3337 - print_function_rettype(&buf, proctup); -
3338 - appendStringInfoChar(&buf, '\n'); -
3339 - } -
3340 - -
3341 - print_function_trftypes(&buf, proctup); -
3342 - -
3343 - appendStringInfo(&buf, " LANGUAGE %s\n", -
3344 - quote_identifier(get_language_name(proc->prolang, false))); -
3345 - -
3346 - /* Emit some miscellaneous options on one line */ -
3347 - oldlen = buf.len; -
3348 - -
3349 - if (proc->prokind == PROKIND_WINDOW) -
3350 - appendStringInfoString(&buf, " WINDOW"); -
3351 - switch (proc->provolatile) -
3352 - { -
3353 - case PROVOLATILE_IMMUTABLE: -
3354 - appendStringInfoString(&buf, " IMMUTABLE"); -
3355 - break; -
3356 - case PROVOLATILE_STABLE: -
3357 - appendStringInfoString(&buf, " STABLE"); -
3358 - break; -
3359 - case PROVOLATILE_VOLATILE: -
3360 - break; -
3361 - } -
3362 - -
3363 - switch (proc->proparallel) -
3364 - { -
3365 - case PROPARALLEL_SAFE: -
3366 - appendStringInfoString(&buf, " PARALLEL SAFE"); -
3367 - break; -
3368 - case PROPARALLEL_RESTRICTED: -
3369 - appendStringInfoString(&buf, " PARALLEL RESTRICTED"); -
3370 - break; -
3371 - case PROPARALLEL_UNSAFE: -
3372 - break; -
3373 - } -
3374 - -
3375 - if (proc->proisstrict) -
3376 - appendStringInfoString(&buf, " STRICT"); -
3377 - if (proc->prosecdef) -
3378 - appendStringInfoString(&buf, " SECURITY DEFINER"); -
3379 - if (proc->proleakproof) -
3380 - appendStringInfoString(&buf, " LEAKPROOF"); -
3381 - -
3382 - /* This code for the default cost and rows should match functioncmds.c */ -
3383 - if (proc->prolang == INTERNALlanguageId || -
3384 - proc->prolang == ClanguageId) -
3385 - procost = 1; -
3386 - else -
3387 - procost = 100; -
3388 - if (proc->procost != procost) -
3389 - appendStringInfo(&buf, " COST %g", proc->procost); -
3390 - -
3391 - if (proc->prorows > 0 && proc->prorows != 1000) -
3392 - appendStringInfo(&buf, " ROWS %g", proc->prorows); -
3393 - -
3394 - if (proc->prosupport) -
3395 - { -
3396 - Oid argtypes[1]; -
3397 - -
3398 - /* -
3399 - * We should qualify the support function's name if it wouldn't be -
3400 - * resolved by lookup in the current search path. -
3401 - */ -
3402 - argtypes[0] = INTERNALOID; -
3403 - appendStringInfo(&buf, " SUPPORT %s", -
3404 - generate_function_name(proc->prosupport, 1, -
3405 - NIL, argtypes, -
3406 - false, NULL, false, false)); 7f135d9Recognize row pattern navigation operations by name in DEFINE
3407 - } -
3408 - -
3409 - if (oldlen != buf.len) -
3410 - appendStringInfoChar(&buf, '\n'); -
3411 - -
3412 - /* Emit any proconfig options, one per line */ -
3413 - tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull); -
3414 - if (!isnull) -
3415 - { -
3416 - ArrayType *a = DatumGetArrayTypeP(tmp); -
3417 - int i; -
3418 - -
3419 - Assert(ARR_ELEMTYPE(a) == TEXTOID); -
3420 - Assert(ARR_NDIM(a) == 1); -
3421 - Assert(ARR_LBOUND(a)[0] == 1); -
3422 - -
3423 - for (i = 1; i <= ARR_DIMS(a)[0]; i++) -
3424 - { -
3425 - Datum d; -
3426 - -
3427 - d = array_ref(a, 1, &i, -
3428 - -1 /* varlenarray */ , -
3429 - -1 /* TEXT's typlen */ , -
3430 - false /* TEXT's typbyval */ , -
3431 - TYPALIGN_INT /* TEXT's typalign */ , -
3432 - &isnull); -
3433 - if (!isnull) -
3434 - { -
3435 - char *configitem = TextDatumGetCString(d); -
3436 - char *pos; -
3437 - -
3438 - pos = strchr(configitem, '='); -
3439 - if (pos == NULL) -
3440 - continue; -
3441 - *pos++ = '\0'; -
3442 - -
3443 - appendStringInfo(&buf, " SET %s TO ", -
3444 - quote_identifier(configitem)); -
3445 - -
3446 - /* -
3447 - * Variables that are marked GUC_LIST_QUOTE were already fully -
3448 - * quoted by flatten_set_variable_args() before they were put -
3449 - * into the proconfig array. However, because the quoting -
3450 - * rules used there aren't exactly like SQL's, we have to -
3451 - * break the list value apart and then quote the elements as -
3452 - * string literals. (The elements may be double-quoted as-is, -
3453 - * but we can't just feed them to the SQL parser; it would do -
3454 - * the wrong thing with elements that are zero-length or -
3455 - * longer than NAMEDATALEN.) Also, we need a special case for -
3456 - * empty lists. -
3457 - * -
3458 - * Variables that are not so marked should just be emitted as -
3459 - * simple string literals. If the variable is not known to -
3460 - * guc.c, we'll do that; this makes it unsafe to use -
3461 - * GUC_LIST_QUOTE for extension variables. -
3462 - */ -
3463 - if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE) -
3464 - { -
3465 - List *namelist; -
3466 - ListCell *lc; -
3467 - -
3468 - /* Parse string into list of identifiers */ -
3469 - if (!SplitGUCList(pos, ',', &namelist)) -
3470 - { -
3471 - /* this shouldn't fail really */ -
3472 - elog(ERROR, "invalid list syntax in proconfig item"); -
3473 - } -
3474 - /* Special case: represent an empty list as NULL */ -
3475 - if (namelist == NIL) -
3476 - appendStringInfoString(&buf, "NULL"); -
3477 - foreach(lc, namelist) -
3478 - { -
3479 - char *curname = (char *) lfirst(lc); -
3480 - -
3481 - simple_quote_literal(&buf, curname); -
3482 - if (lnext(namelist, lc)) -
3483 - appendStringInfoString(&buf, ", "); -
3484 - } -
3485 - } -
3486 - else -
3487 - simple_quote_literal(&buf, pos); -
3488 - appendStringInfoChar(&buf, '\n'); -
3489 - } -
3490 - } -
3491 - } -
3492 - -
3493 - /* And finally the function definition ... */ -
3494 - (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull); -
3495 - if (proc->prolang == SQLlanguageId && !isnull) -
3496 - { -
3497 - print_function_sqlbody(&buf, proctup); -
3498 - } -
3499 - else -
3500 - { -
3501 - appendStringInfoString(&buf, "AS "); -
3502 - -
3503 - tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull); -
3504 - if (!isnull) -
3505 - { -
3506 - simple_quote_literal(&buf, TextDatumGetCString(tmp)); -
3507 - appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */ -
3508 - } -
3509 - -
3510 - tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc); -
3511 - prosrc = TextDatumGetCString(tmp); -
3512 - -
3513 - /* -
3514 - * We always use dollar quoting. Figure out a suitable delimiter. -
3515 - * -
3516 - * Since the user is likely to be editing the function body string, we -
3517 - * shouldn't use a short delimiter that he might easily create a -
3518 - * conflict with. Hence prefer "$function$"/"$procedure$", but extend -
3519 - * if needed. -
3520 - */ -
3521 - initStringInfo(&dq); -
3522 - appendStringInfoChar(&dq, '$'); -
3523 - appendStringInfoString(&dq, (isfunction ? "function" : "procedure")); -
3524 - while (strstr(prosrc, dq.data) != NULL) -
3525 - appendStringInfoChar(&dq, 'x'); -
3526 - appendStringInfoChar(&dq, '$'); -
3527 - -
3528 - appendBinaryStringInfo(&buf, dq.data, dq.len); -
3529 - appendStringInfoString(&buf, prosrc); -
3530 - appendBinaryStringInfo(&buf, dq.data, dq.len); -
3531 - } -
3532 - -
3533 - appendStringInfoChar(&buf, '\n'); -
3534 - -
3535 - ReleaseSysCache(proctup); -
3536 - -
3537 - PG_RETURN_TEXT_P(string_to_text(buf.data)); -
3538 - } -
deparse_expression_pretty() lines 4040-4066
Modified Lines Coverage: 1/1 lines (100.0%)
LineHitsSourceCommit
4040 - deparse_expression_pretty(Node *expr, List *dpcontext, -
4041 - bool forceprefix, bool showimplicit, -
4042 - int prettyFlags, int startIndent) -
4043 - { -
4044 - StringInfoData buf; -
4045 - deparse_context context; -
4046 - -
4047 - initStringInfo(&buf); -
4048 - context.buf = &buf; -
4049 - context.namespaces = dpcontext; -
4050 - context.resultDesc = NULL; -
4051 - context.targetList = NIL; -
4052 - context.windowClause = NIL; -
4053 - context.varprefix = forceprefix; -
4054 - context.prettyFlags = prettyFlags; -
4055 - context.wrapColumn = WRAP_COLUMN_DEFAULT; -
4056 - context.indentLevel = startIndent; -
4057 - context.colNamesVisible = true; -
4058 - context.inGroupBy = false; -
4059 64593 context.inRPRDefine = false; 7f135d9Recognize row pattern navigation operations by name in DEFINE
4060 - context.varInOrderBy = false; -
4061 - context.appendparents = NULL; -
4062 - -
4063 - get_rule_expr(expr, &context, showimplicit); -
4064 - -
4065 - return buf.data; -
4066 - } -
make_ruledef() lines 5716-5900
Modified Lines Coverage: 1/1 lines (100.0%)
LineHitsSourceCommit
5716 - make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc, -
5717 - int prettyFlags) -
5718 - { -
5719 - char *rulename; -
5720 - char ev_type; -
5721 - Oid ev_class; -
5722 - bool is_instead; -
5723 - char *ev_qual; -
5724 - char *ev_action; -
5725 - List *actions; -
5726 - Relation ev_relation; -
5727 - TupleDesc viewResultDesc = NULL; -
5728 - int fno; -
5729 - Datum dat; -
5730 - bool isnull; -
5731 - -
5732 - /* -
5733 - * Get the attribute values from the rules tuple -
5734 - */ -
5735 - fno = SPI_fnumber(rulettc, "rulename"); -
5736 - dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); -
5737 - Assert(!isnull); -
5738 - rulename = NameStr(*(DatumGetName(dat))); -
5739 - -
5740 - fno = SPI_fnumber(rulettc, "ev_type"); -
5741 - dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); -
5742 - Assert(!isnull); -
5743 - ev_type = DatumGetChar(dat); -
5744 - -
5745 - fno = SPI_fnumber(rulettc, "ev_class"); -
5746 - dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); -
5747 - Assert(!isnull); -
5748 - ev_class = DatumGetObjectId(dat); -
5749 - -
5750 - fno = SPI_fnumber(rulettc, "is_instead"); -
5751 - dat = SPI_getbinval(ruletup, rulettc, fno, &isnull); -
5752 - Assert(!isnull); -
5753 - is_instead = DatumGetBool(dat); -
5754 - -
5755 - fno = SPI_fnumber(rulettc, "ev_qual"); -
5756 - ev_qual = SPI_getvalue(ruletup, rulettc, fno); -
5757 - Assert(ev_qual != NULL); -
5758 - -
5759 - fno = SPI_fnumber(rulettc, "ev_action"); -
5760 - ev_action = SPI_getvalue(ruletup, rulettc, fno); -
5761 - Assert(ev_action != NULL); -
5762 - actions = (List *) stringToNode(ev_action); -
5763 - if (actions == NIL) -
5764 - elog(ERROR, "invalid empty ev_action list"); -
5765 - -
5766 - ev_relation = table_open(ev_class, AccessShareLock); -
5767 - -
5768 - /* -
5769 - * Build the rules definition text -
5770 - */ -
5771 - appendStringInfo(buf, "CREATE RULE %s AS", -
5772 - quote_identifier(rulename)); -
5773 - -
5774 - if (prettyFlags & PRETTYFLAG_INDENT) -
5775 - appendStringInfoString(buf, "\n ON "); -
5776 - else -
5777 - appendStringInfoString(buf, " ON "); -
5778 - -
5779 - /* The event the rule is fired for */ -
5780 - switch (ev_type) -
5781 - { -
5782 - case '1': -
5783 - appendStringInfoString(buf, "SELECT"); -
5784 - viewResultDesc = RelationGetDescr(ev_relation); -
5785 - break; -
5786 - -
5787 - case '2': -
5788 - appendStringInfoString(buf, "UPDATE"); -
5789 - break; -
5790 - -
5791 - case '3': -
5792 - appendStringInfoString(buf, "INSERT"); -
5793 - break; -
5794 - -
5795 - case '4': -
5796 - appendStringInfoString(buf, "DELETE"); -
5797 - break; -
5798 - -
5799 - default: -
5800 - ereport(ERROR, -
5801 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
5802 - errmsg("rule \"%s\" has unsupported event type %d", -
5803 - rulename, ev_type))); -
5804 - break; -
5805 - } -
5806 - -
5807 - /* The relation the rule is fired on */ -
5808 - appendStringInfo(buf, " TO %s", -
5809 - (prettyFlags & PRETTYFLAG_SCHEMA) ? -
5810 - generate_relation_name(ev_class, NIL) : -
5811 - generate_qualified_relation_name(ev_class)); -
5812 - -
5813 - /* If the rule has an event qualification, add it */ -
5814 - if (strcmp(ev_qual, "<>") != 0) -
5815 - { -
5816 - Node *qual; -
5817 - Query *query; -
5818 - deparse_context context; -
5819 - deparse_namespace dpns; -
5820 - -
5821 - if (prettyFlags & PRETTYFLAG_INDENT) -
5822 - appendStringInfoString(buf, "\n "); -
5823 - appendStringInfoString(buf, " WHERE "); -
5824 - -
5825 - qual = stringToNode(ev_qual); -
5826 - -
5827 - /* -
5828 - * We need to make a context for recognizing any Vars in the qual -
5829 - * (which can only be references to OLD and NEW). Use the rtable of -
5830 - * the first query in the action list for this purpose. -
5831 - */ -
5832 - query = (Query *) linitial(actions); -
5833 - -
5834 - /* -
5835 - * If the action is INSERT...SELECT, OLD/NEW have been pushed down -
5836 - * into the SELECT, and that's what we need to look at. (Ugly kluge -
5837 - * ... try to fix this when we redesign querytrees.) -
5838 - */ -
5839 - query = getInsertSelectQuery(query, NULL); -
5840 - -
5841 - /* Must acquire locks right away; see notes in get_query_def() */ -
5842 - AcquireRewriteLocks(query, false, false); -
5843 - -
5844 - context.buf = buf; -
5845 - context.namespaces = list_make1(&dpns); -
5846 - context.resultDesc = NULL; -
5847 - context.targetList = NIL; -
5848 - context.windowClause = NIL; -
5849 - context.varprefix = (list_length(query->rtable) != 1); -
5850 - context.prettyFlags = prettyFlags; -
5851 - context.wrapColumn = WRAP_COLUMN_DEFAULT; -
5852 - context.indentLevel = PRETTYINDENT_STD; -
5853 - context.colNamesVisible = true; -
5854 - context.inGroupBy = false; -
5855 62 context.inRPRDefine = false; 7f135d9Recognize row pattern navigation operations by name in DEFINE
5856 - context.varInOrderBy = false; -
5857 - context.appendparents = NULL; -
5858 - -
5859 - set_deparse_for_query(&dpns, query, NIL); -
5860 - -
5861 - get_rule_expr(qual, &context, false); -
5862 - } -
5863 - -
5864 - appendStringInfoString(buf, " DO "); -
5865 - -
5866 - /* The INSTEAD keyword (if so) */ -
5867 - if (is_instead) -
5868 - appendStringInfoString(buf, "INSTEAD "); -
5869 - -
5870 - /* Finally the rules actions */ -
5871 - if (list_length(actions) > 1) -
5872 - { -
5873 - ListCell *action; -
5874 - Query *query; -
5875 - -
5876 - appendStringInfoChar(buf, '('); -
5877 - foreach(action, actions) -
5878 - { -
5879 - query = (Query *) lfirst(action); -
5880 - get_query_def(query, buf, NIL, viewResultDesc, true, -
5881 - prettyFlags, WRAP_COLUMN_DEFAULT, 0); -
5882 - if (prettyFlags) -
5883 - appendStringInfoString(buf, ";\n"); -
5884 - else -
5885 - appendStringInfoString(buf, "; "); -
5886 - } -
5887 - appendStringInfoString(buf, ");"); -
5888 - } -
5889 - else -
5890 - { -
5891 - Query *query; -
5892 - -
5893 - query = (Query *) linitial(actions); -
5894 - get_query_def(query, buf, NIL, viewResultDesc, true, -
5895 - prettyFlags, WRAP_COLUMN_DEFAULT, 0); -
5896 - appendStringInfoChar(buf, ';'); -
5897 - } -
5898 - -
5899 - table_close(ev_relation, AccessShareLock); -
5900 - } -
get_query_def() lines 5994-6091
Modified Lines Coverage: 1/1 lines (100.0%)
LineHitsSourceCommit
5994 - get_query_def(Query *query, StringInfo buf, List *parentnamespace, -
5995 - TupleDesc resultDesc, bool colNamesVisible, -
5996 - int prettyFlags, int wrapColumn, int startIndent) -
5997 - { -
5998 - deparse_context context; -
5999 - deparse_namespace dpns; -
6000 - int rtable_size; -
6001 - -
6002 - /* Guard against excessively long or deeply-nested queries */ -
6003 - CHECK_FOR_INTERRUPTS(); -
6004 - check_stack_depth(); -
6005 - -
6006 - rtable_size = query->hasGroupRTE ? -
6007 - list_length(query->rtable) - 1 : -
6008 - list_length(query->rtable); -
6009 - -
6010 - /* -
6011 - * Replace any Vars in the query's targetlist and havingQual that -
6012 - * reference GROUP outputs with the underlying grouping expressions. -
6013 - * -
6014 - * We can safely pass NULL for the root here. Preserving varnullingrels -
6015 - * makes no difference to the deparsed source text. -
6016 - */ -
6017 - if (query->hasGroupRTE) -
6018 - { -
6019 - query->targetList = (List *) -
6020 - flatten_group_exprs(NULL, query, (Node *) query->targetList); -
6021 - query->havingQual = -
6022 - flatten_group_exprs(NULL, query, query->havingQual); -
6023 - } -
6024 - -
6025 - /* -
6026 - * Before we begin to examine the query, acquire locks on referenced -
6027 - * relations, and fix up deleted columns in JOIN RTEs. This ensures -
6028 - * consistent results. Note we assume it's OK to scribble on the passed -
6029 - * querytree! -
6030 - * -
6031 - * We are only deparsing the query (we are not about to execute it), so we -
6032 - * only need AccessShareLock on the relations it mentions. -
6033 - */ -
6034 - AcquireRewriteLocks(query, false, false); -
6035 - -
6036 - context.buf = buf; -
6037 - context.namespaces = lcons(&dpns, list_copy(parentnamespace)); -
6038 - context.resultDesc = NULL; -
6039 - context.targetList = NIL; -
6040 - context.windowClause = NIL; -
6041 - context.varprefix = (parentnamespace != NIL || -
6042 - rtable_size != 1); -
6043 - context.prettyFlags = prettyFlags; -
6044 - context.wrapColumn = wrapColumn; -
6045 - context.indentLevel = startIndent; -
6046 - context.colNamesVisible = colNamesVisible; -
6047 - context.inGroupBy = false; -
6048 5651 context.inRPRDefine = false; 7f135d9Recognize row pattern navigation operations by name in DEFINE
6049 - context.varInOrderBy = false; -
6050 - context.appendparents = NULL; -
6051 - -
6052 - set_deparse_for_query(&dpns, query, parentnamespace); -
6053 - -
6054 - switch (query->commandType) -
6055 - { -
6056 - case CMD_SELECT: -
6057 - /* We set context.resultDesc only if it's a SELECT */ -
6058 - context.resultDesc = resultDesc; -
6059 - get_select_query_def(query, &context); -
6060 - break; -
6061 - -
6062 - case CMD_UPDATE: -
6063 - get_update_query_def(query, &context); -
6064 - break; -
6065 - -
6066 - case CMD_INSERT: -
6067 - get_insert_query_def(query, &context); -
6068 - break; -
6069 - -
6070 - case CMD_DELETE: -
6071 - get_delete_query_def(query, &context); -
6072 - break; -
6073 - -
6074 - case CMD_MERGE: -
6075 - get_merge_query_def(query, &context); -
6076 - break; -
6077 - -
6078 - case CMD_NOTHING: -
6079 - appendStringInfoString(buf, "NOTHING"); -
6080 - break; -
6081 - -
6082 - case CMD_UTILITY: -
6083 - get_utility_query_def(query, &context); -
6084 - break; -
6085 - -
6086 - default: -
6087 - elog(ERROR, "unrecognized query command type: %d", -
6088 - query->commandType); -
6089 - break; -
6090 - } -
6091 - } -
append_pattern_quantifier() lines 7123-7152
Modified Lines Coverage: 19/19 lines (100.0%)
LineHitsSourceCommit
7123 8569 append_pattern_quantifier(StringInfo buf, RPRPatternNode *node) 1f4b53eRow pattern recognition patch (rewriter).
7124 - { 1f4b53eRow pattern recognition patch (rewriter).
7125 8569 bool has_quantifier = true; 1f4b53eRow pattern recognition patch (rewriter).
7126 - 1f4b53eRow pattern recognition patch (rewriter).
7127 8569 if (node->min == 1 && node->max == 1) 1f4b53eRow pattern recognition patch (rewriter).
7128 - { 1f4b53eRow pattern recognition patch (rewriter).
7129 - /* {1,1} = no quantifier */ 1f4b53eRow pattern recognition patch (rewriter).
7130 - has_quantifier = false; 1f4b53eRow pattern recognition patch (rewriter).
7131 - } 1f4b53eRow pattern recognition patch (rewriter).
7132 2157 else if (node->min == 0 && node->max == PG_INT32_MAX) 1f4b53eRow pattern recognition patch (rewriter).
7133 247 appendStringInfoChar(buf, '*'); 1f4b53eRow pattern recognition patch (rewriter).
7134 1910 else if (node->min == 1 && node->max == PG_INT32_MAX) 1f4b53eRow pattern recognition patch (rewriter).
7135 1328 appendStringInfoChar(buf, '+'); 1f4b53eRow pattern recognition patch (rewriter).
7136 582 else if (node->min == 0 && node->max == 1) 1f4b53eRow pattern recognition patch (rewriter).
7137 148 appendStringInfoChar(buf, '?'); 1f4b53eRow pattern recognition patch (rewriter).
7138 434 else if (node->max == PG_INT32_MAX) 1f4b53eRow pattern recognition patch (rewriter).
7139 78 appendStringInfo(buf, "{%d,}", node->min); 1f4b53eRow pattern recognition patch (rewriter).
7140 356 else if (node->min == node->max) 1f4b53eRow pattern recognition patch (rewriter).
7141 162 appendStringInfo(buf, "{%d}", node->min); 1f4b53eRow pattern recognition patch (rewriter).
7142 - else 1f4b53eRow pattern recognition patch (rewriter).
7143 194 appendStringInfo(buf, "{%d,%d}", node->min, node->max); 1f4b53eRow pattern recognition patch (rewriter).
7144 - 1f4b53eRow pattern recognition patch (rewriter).
7145 8569 if (node->reluctant) 1f4b53eRow pattern recognition patch (rewriter).
7146 - { 1f4b53eRow pattern recognition patch (rewriter).
7147 108 if (!has_quantifier) 1f4b53eRow pattern recognition patch (rewriter).
7148 9 appendStringInfoString(buf, "{1}"); /* make reluctant ? 1f4b53eRow pattern recognition patch (rewriter).
7149 - * unambiguous */ 1f4b53eRow pattern recognition patch (rewriter).
7150 108 appendStringInfoChar(buf, '?'); 1f4b53eRow pattern recognition patch (rewriter).
7151 - } 1f4b53eRow pattern recognition patch (rewriter).
7152 8569 } 1f4b53eRow pattern recognition patch (rewriter).
get_rule_pattern_node() lines 7158-7205
Modified Lines Coverage: 28/28 lines (100.0%)
LineHitsSourceCommit
7158 12290 get_rule_pattern_node(RPRPatternNode *node, deparse_context *context) 1f4b53eRow pattern recognition patch (rewriter).
7159 - { 1f4b53eRow pattern recognition patch (rewriter).
7160 12290 StringInfo buf = context->buf; 1f4b53eRow pattern recognition patch (rewriter).
7161 12290 const char *sep; 1f4b53eRow pattern recognition patch (rewriter).
7162 - 1f4b53eRow pattern recognition patch (rewriter).
7163 12290 Assert(node != NULL); 1f4b53eRow pattern recognition patch (rewriter).
7164 - 1f4b53eRow pattern recognition patch (rewriter).
7165 12290 switch (node->nodeType) 1f4b53eRow pattern recognition patch (rewriter).
7166 - { 1f4b53eRow pattern recognition patch (rewriter).
7167 6840 case RPR_PATTERN_VAR: 1f4b53eRow pattern recognition patch (rewriter).
7168 6840 appendStringInfoString(buf, quote_identifier(node->varName)); 1f4b53eRow pattern recognition patch (rewriter).
7169 6840 append_pattern_quantifier(buf, node); 1f4b53eRow pattern recognition patch (rewriter).
7170 6840 break; 1f4b53eRow pattern recognition patch (rewriter).
7171 - 1f4b53eRow pattern recognition patch (rewriter).
7172 - case RPR_PATTERN_SEQ: 1f4b53eRow pattern recognition patch (rewriter).
7173 - sep = ""; 1f4b53eRow pattern recognition patch (rewriter).
7174 7139 foreach_node(RPRPatternNode, child, node->children) 1f4b53eRow pattern recognition patch (rewriter).
7175 - { 1f4b53eRow pattern recognition patch (rewriter).
7176 5008 appendStringInfoString(buf, sep); 1f4b53eRow pattern recognition patch (rewriter).
7177 5008 get_rule_pattern_node(child, context); 1f4b53eRow pattern recognition patch (rewriter).
7178 5008 sep = " "; 1f4b53eRow pattern recognition patch (rewriter).
7179 - } 1f4b53eRow pattern recognition patch (rewriter).
7180 - break; 1f4b53eRow pattern recognition patch (rewriter).
7181 - 1f4b53eRow pattern recognition patch (rewriter).
7182 - case RPR_PATTERN_ALT: 1f4b53eRow pattern recognition patch (rewriter).
7183 - sep = ""; 1f4b53eRow pattern recognition patch (rewriter).
7184 4924 foreach_node(RPRPatternNode, child, node->children) 1f4b53eRow pattern recognition patch (rewriter).
7185 - { 1f4b53eRow pattern recognition patch (rewriter).
7186 3334 appendStringInfoString(buf, sep); 1f4b53eRow pattern recognition patch (rewriter).
7187 3334 get_rule_pattern_node(child, context); 1f4b53eRow pattern recognition patch (rewriter).
7188 3334 sep = " | "; 1f4b53eRow pattern recognition patch (rewriter).
7189 - } 1f4b53eRow pattern recognition patch (rewriter).
7190 - break; 1f4b53eRow pattern recognition patch (rewriter).
7191 - 1f4b53eRow pattern recognition patch (rewriter).
7192 1729 case RPR_PATTERN_GROUP: 1f4b53eRow pattern recognition patch (rewriter).
7193 1729 appendStringInfoChar(buf, '('); 1f4b53eRow pattern recognition patch (rewriter).
7194 1729 sep = ""; 1f4b53eRow pattern recognition patch (rewriter).
7195 3458 foreach_node(RPRPatternNode, child, node->children) 1f4b53eRow pattern recognition patch (rewriter).
7196 - { 1f4b53eRow pattern recognition patch (rewriter).
7197 1729 appendStringInfoString(buf, sep); 1f4b53eRow pattern recognition patch (rewriter).
7198 1729 get_rule_pattern_node(child, context); 1f4b53eRow pattern recognition patch (rewriter).
7199 1729 sep = " "; 1f4b53eRow pattern recognition patch (rewriter).
7200 - } 1f4b53eRow pattern recognition patch (rewriter).
7201 1729 appendStringInfoChar(buf, ')'); 1f4b53eRow pattern recognition patch (rewriter).
7202 1729 append_pattern_quantifier(buf, node); 1f4b53eRow pattern recognition patch (rewriter).
7203 1729 break; 1f4b53eRow pattern recognition patch (rewriter).
7204 - } 1f4b53eRow pattern recognition patch (rewriter).
7205 12290 } 1f4b53eRow pattern recognition patch (rewriter).
get_rule_pattern() lines 7211-7218
Modified Lines Coverage: 6/6 lines (100.0%)
LineHitsSourceCommit
7211 2219 get_rule_pattern(RPRPatternNode *rpPattern, deparse_context *context) 1f4b53eRow pattern recognition patch (rewriter).
7212 - { 1f4b53eRow pattern recognition patch (rewriter).
7213 2219 StringInfo buf = context->buf; 1f4b53eRow pattern recognition patch (rewriter).
7214 - 1f4b53eRow pattern recognition patch (rewriter).
7215 2219 appendStringInfoChar(buf, '('); 1f4b53eRow pattern recognition patch (rewriter).
7216 2219 get_rule_pattern_node(rpPattern, context); 1f4b53eRow pattern recognition patch (rewriter).
7217 2219 appendStringInfoChar(buf, ')'); 1f4b53eRow pattern recognition patch (rewriter).
7218 2219 } 1f4b53eRow pattern recognition patch (rewriter).
get_rule_define() lines 7224-7247
Modified Lines Coverage: 12/12 lines (100.0%)
LineHitsSourceCommit
7224 2219 get_rule_define(List *defineClause, deparse_context *context) 1f4b53eRow pattern recognition patch (rewriter).
7225 - { 1f4b53eRow pattern recognition patch (rewriter).
7226 2219 StringInfo buf = context->buf; 1f4b53eRow pattern recognition patch (rewriter).
7227 2219 const char *sep; 1f4b53eRow pattern recognition patch (rewriter).
7228 2219 bool save_inrprdefine = context->inRPRDefine; 7f135d9Recognize row pattern navigation operations by name in DEFINE
7229 - 1f4b53eRow pattern recognition patch (rewriter).
7230 2219 sep = " "; 1f4b53eRow pattern recognition patch (rewriter).
7231 - 1f4b53eRow pattern recognition patch (rewriter).
7232 - /* 7f135d9Recognize row pattern navigation operations by name in DEFINE
7233 - * Within the DEFINE clause an unqualified prev/next/first/last is a 7f135d9Recognize row pattern navigation operations by name in DEFINE
7234 - * navigation operation, so a user function of one of those names must be 7f135d9Recognize row pattern navigation operations by name in DEFINE
7235 - * schema-qualified to survive a reparse; see generate_function_name(). 7f135d9Recognize row pattern navigation operations by name in DEFINE
7236 - */ 7f135d9Recognize row pattern navigation operations by name in DEFINE
7237 2219 context->inRPRDefine = true; 7f135d9Recognize row pattern navigation operations by name in DEFINE
7238 - 7f135d9Recognize row pattern navigation operations by name in DEFINE
7239 8453 foreach_node(TargetEntry, te, defineClause) 1f4b53eRow pattern recognition patch (rewriter).
7240 - { 1f4b53eRow pattern recognition patch (rewriter).
7241 6234 appendStringInfo(buf, "%s%s AS ", sep, quote_identifier(te->resname)); 1f4b53eRow pattern recognition patch (rewriter).
7242 6234 get_rule_expr((Node *) te->expr, context, false); 1f4b53eRow pattern recognition patch (rewriter).
7243 6234 sep = ",\n "; 1f4b53eRow pattern recognition patch (rewriter).
7244 - } 1f4b53eRow pattern recognition patch (rewriter).
7245 - 7f135d9Recognize row pattern navigation operations by name in DEFINE
7246 2219 context->inRPRDefine = save_inrprdefine; 7f135d9Recognize row pattern navigation operations by name in DEFINE
7247 2219 } 1f4b53eRow pattern recognition patch (rewriter).
get_rule_windowspec() lines 7288-7383
Modified Lines Coverage: 28/28 lines (100.0%)
LineHitsSourceCommit
7288 - get_rule_windowspec(WindowClause *wc, List *targetList, -
7289 - deparse_context *context) -
7290 - { -
7291 - StringInfo buf = context->buf; -
7292 - bool needspace = false; -
7293 - const char *sep; -
7294 - ListCell *l; -
7295 - -
7296 - appendStringInfoChar(buf, '('); -
7297 - if (wc->refname) -
7298 - { -
7299 - appendStringInfoString(buf, quote_identifier(wc->refname)); -
7300 - needspace = true; -
7301 - } -
7302 - /* partition clauses are always inherited, so only print if no refname */ -
7303 - if (wc->partitionClause && !wc->refname) -
7304 - { -
7305 - if (needspace) -
7306 - appendStringInfoChar(buf, ' '); -
7307 - appendStringInfoString(buf, "PARTITION BY "); -
7308 - sep = ""; -
7309 - foreach(l, wc->partitionClause) -
7310 - { -
7311 - SortGroupClause *grp = (SortGroupClause *) lfirst(l); -
7312 - -
7313 - appendStringInfoString(buf, sep); -
7314 - get_rule_sortgroupclause(grp->tleSortGroupRef, targetList, -
7315 - false, context); -
7316 - sep = ", "; -
7317 - } -
7318 - needspace = true; -
7319 - } -
7320 - /* print ordering clause only if not inherited */ -
7321 - if (wc->orderClause && !wc->copiedOrder) -
7322 - { -
7323 - if (needspace) -
7324 - appendStringInfoChar(buf, ' '); -
7325 - appendStringInfoString(buf, "ORDER BY "); -
7326 - get_rule_orderby(wc->orderClause, targetList, false, context); -
7327 - needspace = true; -
7328 - } -
7329 - /* framing clause is never inherited, so print unless it's default */ -
7330 - if (wc->frameOptions & FRAMEOPTION_NONDEFAULT) -
7331 - { -
7332 - if (needspace) -
7333 - appendStringInfoChar(buf, ' '); -
7334 - get_window_frame_options(wc->frameOptions, -
7335 - wc->startOffset, wc->endOffset, -
7336 - context); -
7337 2257 needspace = true; 1f4b53eRow pattern recognition patch (rewriter).
7338 - } -
7339 - 1f4b53eRow pattern recognition patch (rewriter).
7340 - /* RPR */ 1f4b53eRow pattern recognition patch (rewriter).
7341 2261 if (wc->rpSkipTo == ST_NEXT_ROW) 1f4b53eRow pattern recognition patch (rewriter).
7342 - { 1f4b53eRow pattern recognition patch (rewriter).
7343 79 if (needspace) 1f4b53eRow pattern recognition patch (rewriter).
7344 79 appendStringInfoChar(buf, ' '); 1f4b53eRow pattern recognition patch (rewriter).
7345 79 appendStringInfoString(buf, 1f4b53eRow pattern recognition patch (rewriter).
7346 - "\n AFTER MATCH SKIP TO NEXT ROW "); 1f4b53eRow pattern recognition patch (rewriter).
7347 79 needspace = true; 1f4b53eRow pattern recognition patch (rewriter).
7348 - } 1f4b53eRow pattern recognition patch (rewriter).
7349 2182 else if (wc->rpSkipTo == ST_PAST_LAST_ROW) 1f4b53eRow pattern recognition patch (rewriter).
7350 - { 1f4b53eRow pattern recognition patch (rewriter).
7351 2140 if (needspace) 1f4b53eRow pattern recognition patch (rewriter).
7352 2140 appendStringInfoChar(buf, ' '); 1f4b53eRow pattern recognition patch (rewriter).
7353 2140 appendStringInfoString(buf, 1f4b53eRow pattern recognition patch (rewriter).
7354 - "\n AFTER MATCH SKIP PAST LAST ROW "); 1f4b53eRow pattern recognition patch (rewriter).
7355 2140 needspace = true; 1f4b53eRow pattern recognition patch (rewriter).
7356 - } 1f4b53eRow pattern recognition patch (rewriter).
7357 2261 if (wc->initial) 1f4b53eRow pattern recognition patch (rewriter).
7358 - { 1f4b53eRow pattern recognition patch (rewriter).
7359 2219 if (needspace) 1f4b53eRow pattern recognition patch (rewriter).
7360 2219 appendStringInfoChar(buf, ' '); 1f4b53eRow pattern recognition patch (rewriter).
7361 2219 appendStringInfoString(buf, "\n INITIAL"); 1f4b53eRow pattern recognition patch (rewriter).
7362 2219 needspace = true; 1f4b53eRow pattern recognition patch (rewriter).
7363 - } 1f4b53eRow pattern recognition patch (rewriter).
7364 2261 if (wc->rpPattern) 1f4b53eRow pattern recognition patch (rewriter).
7365 - { 1f4b53eRow pattern recognition patch (rewriter).
7366 2219 if (needspace) 1f4b53eRow pattern recognition patch (rewriter).
7367 2219 appendStringInfoChar(buf, ' '); 1f4b53eRow pattern recognition patch (rewriter).
7368 2219 appendStringInfoString(buf, "\n PATTERN "); 1f4b53eRow pattern recognition patch (rewriter).
7369 2219 get_rule_pattern(wc->rpPattern, context); 1f4b53eRow pattern recognition patch (rewriter).
7370 2219 needspace = true; 1f4b53eRow pattern recognition patch (rewriter).
7371 - } 8b1b342Improve EXPLAIN's display of window functions.
7372 - 1f4b53eRow pattern recognition patch (rewriter).
7373 2261 if (wc->defineClause) 1f4b53eRow pattern recognition patch (rewriter).
7374 - { 1f4b53eRow pattern recognition patch (rewriter).
7375 2219 if (needspace) 1f4b53eRow pattern recognition patch (rewriter).
7376 2219 appendStringInfoChar(buf, ' '); 1f4b53eRow pattern recognition patch (rewriter).
7377 2219 appendStringInfoString(buf, "\n DEFINE\n"); 1f4b53eRow pattern recognition patch (rewriter).
7378 2219 get_rule_define(wc->defineClause, context); 1f4b53eRow pattern recognition patch (rewriter).
7379 2219 appendStringInfoChar(buf, ' '); 1f4b53eRow pattern recognition patch (rewriter).
7380 - } 1f4b53eRow pattern recognition patch (rewriter).
7381 - 1f4b53eRow pattern recognition patch (rewriter).
7382 - appendStringInfoChar(buf, ')'); -
7383 - } -
get_window_frame_options_for_explain() lines 7458-7484
Modified Lines Coverage: 1/1 lines (100.0%)
LineHitsSourceCommit
7458 - get_window_frame_options_for_explain(int frameOptions, -
7459 - Node *startOffset, Node *endOffset, -
7460 - List *dpcontext, bool forceprefix) -
7461 - { -
7462 - StringInfoData buf; -
7463 - deparse_context context; -
7464 - -
7465 - initStringInfo(&buf); -
7466 - context.buf = &buf; -
7467 - context.namespaces = dpcontext; -
7468 - context.resultDesc = NULL; -
7469 - context.targetList = NIL; -
7470 - context.windowClause = NIL; -
7471 - context.varprefix = forceprefix; -
7472 - context.prettyFlags = 0; -
7473 - context.wrapColumn = WRAP_COLUMN_DEFAULT; -
7474 - context.indentLevel = 0; -
7475 - context.colNamesVisible = true; -
7476 - context.inGroupBy = false; -
7477 1226 context.inRPRDefine = false; 7f135d9Recognize row pattern navigation operations by name in DEFINE
7478 - context.varInOrderBy = false; -
7479 - context.appendparents = NULL; -
7480 - -
7481 - get_window_frame_options(frameOptions, startOffset, endOffset, &context); -
7482 - -
7483 - return buf.data; -
7484 - } -
isSimpleNode() lines 9599-9822
Modified Lines Coverage: 0/0 lines (0.0%)
LineHitsSourceCommit
9599 - isSimpleNode(Node *node, Node *parentNode, int prettyFlags) -
9600 - { -
9601 - if (!node) -
9602 - return false; -
9603 - -
9604 - switch (nodeTag(node)) -
9605 - { -
9606 - case T_Var: -
9607 - case T_Const: -
9608 - case T_Param: -
9609 - case T_CoerceToDomainValue: -
9610 - case T_SetToDefault: -
9611 - case T_CurrentOfExpr: -
9612 - /* single words: always simple */ -
9613 - return true; -
9614 - -
9615 - case T_SubscriptingRef: -
9616 - case T_ArrayExpr: -
9617 - case T_RowExpr: -
9618 - case T_CoalesceExpr: -
9619 - case T_MinMaxExpr: -
9620 - case T_SQLValueFunction: -
9621 - case T_XmlExpr: -
9622 - case T_NextValueExpr: -
9623 - case T_NullIfExpr: -
9624 - case T_Aggref: -
9625 - case T_GroupingFunc: -
9626 - case T_WindowFunc: -
9627 - case T_MergeSupportFunc: -
9628 - case T_FuncExpr: -
9629 - case T_JsonConstructorExpr: -
9630 - case T_JsonExpr: -
9631 - case T_RPRNavExpr: 1f4b53eRow pattern recognition patch (rewriter).
9632 - /* function-like: name(..) or name[..] */ -
9633 - return true; -
9634 - -
9635 - /* CASE keywords act as parentheses */ -
9636 - case T_CaseExpr: -
9637 - return true; -
9638 - -
9639 - case T_FieldSelect: -
9640 - -
9641 - /* -
9642 - * appears simple since . has top precedence, unless parent is -
9643 - * T_FieldSelect itself! -
9644 - */ -
9645 - return !IsA(parentNode, FieldSelect); -
9646 - -
9647 - case T_FieldStore: -
9648 - -
9649 - /* -
9650 - * treat like FieldSelect (probably doesn't matter) -
9651 - */ -
9652 - return !IsA(parentNode, FieldStore); -
9653 - -
9654 - case T_CoerceToDomain: -
9655 - /* maybe simple, check args */ -
9656 - return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg, -
9657 - node, prettyFlags); -
9658 - case T_RelabelType: -
9659 - return isSimpleNode((Node *) ((RelabelType *) node)->arg, -
9660 - node, prettyFlags); -
9661 - case T_CoerceViaIO: -
9662 - return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg, -
9663 - node, prettyFlags); -
9664 - case T_ArrayCoerceExpr: -
9665 - return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg, -
9666 - node, prettyFlags); -
9667 - case T_ConvertRowtypeExpr: -
9668 - return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg, -
9669 - node, prettyFlags); -
9670 - case T_ReturningExpr: -
9671 - return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr, -
9672 - node, prettyFlags); -
9673 - -
9674 - case T_OpExpr: -
9675 - { -
9676 - /* depends on parent node type; needs further checking */ -
9677 - if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr)) -
9678 - { -
9679 - const char *op; -
9680 - const char *parentOp; -
9681 - bool is_lopriop; -
9682 - bool is_hipriop; -
9683 - bool is_lopriparent; -
9684 - bool is_hipriparent; -
9685 - -
9686 - op = get_simple_binary_op_name((OpExpr *) node); -
9687 - if (!op) -
9688 - return false; -
9689 - -
9690 - /* We know only the basic operators + - and * / % */ -
9691 - is_lopriop = (strchr("+-", *op) != NULL); -
9692 - is_hipriop = (strchr("*/%", *op) != NULL); -
9693 - if (!(is_lopriop || is_hipriop)) -
9694 - return false; -
9695 - -
9696 - parentOp = get_simple_binary_op_name((OpExpr *) parentNode); -
9697 - if (!parentOp) -
9698 - return false; -
9699 - -
9700 - is_lopriparent = (strchr("+-", *parentOp) != NULL); -
9701 - is_hipriparent = (strchr("*/%", *parentOp) != NULL); -
9702 - if (!(is_lopriparent || is_hipriparent)) -
9703 - return false; -
9704 - -
9705 - if (is_hipriop && is_lopriparent) -
9706 - return true; /* op binds tighter than parent */ -
9707 - -
9708 - if (is_lopriop && is_hipriparent) -
9709 - return false; -
9710 - -
9711 - /* -
9712 - * Operators are same priority --- can skip parens only if -
9713 - * we have (a - b) - c, not a - (b - c). -
9714 - */ -
9715 - if (node == (Node *) linitial(((OpExpr *) parentNode)->args)) -
9716 - return true; -
9717 - -
9718 - return false; -
9719 - } -
9720 - /* else do the same stuff as for T_SubLink et al. */ -
9721 - } -
9722 - pg_fallthrough; -
9723 - -
9724 - case T_SubLink: -
9725 - case T_NullTest: -
9726 - case T_BooleanTest: -
9727 - case T_DistinctExpr: -
9728 - case T_JsonIsPredicate: -
9729 - switch (nodeTag(parentNode)) -
9730 - { -
9731 - case T_FuncExpr: -
9732 - { -
9733 - /* special handling for casts and COERCE_SQL_SYNTAX */ -
9734 - CoercionForm type = ((FuncExpr *) parentNode)->funcformat; -
9735 - -
9736 - if (type == COERCE_EXPLICIT_CAST || -
9737 - type == COERCE_IMPLICIT_CAST || -
9738 - type == COERCE_SQL_SYNTAX) -
9739 - return false; -
9740 - return true; /* own parentheses */ -
9741 - } -
9742 - case T_BoolExpr: /* lower precedence */ -
9743 - case T_SubscriptingRef: /* other separators */ -
9744 - case T_ArrayExpr: /* other separators */ -
9745 - case T_RowExpr: /* other separators */ -
9746 - case T_CoalesceExpr: /* own parentheses */ -
9747 - case T_MinMaxExpr: /* own parentheses */ -
9748 - case T_XmlExpr: /* own parentheses */ -
9749 - case T_NullIfExpr: /* other separators */ -
9750 - case T_Aggref: /* own parentheses */ -
9751 - case T_GroupingFunc: /* own parentheses */ -
9752 - case T_WindowFunc: /* own parentheses */ -
9753 - case T_CaseExpr: /* other separators */ -
9754 - return true; -
9755 - default: -
9756 - return false; -
9757 - } -
9758 - -
9759 - case T_BoolExpr: -
9760 - switch (nodeTag(parentNode)) -
9761 - { -
9762 - case T_BoolExpr: -
9763 - if (prettyFlags & PRETTYFLAG_PAREN) -
9764 - { -
9765 - BoolExprType type; -
9766 - BoolExprType parentType; -
9767 - -
9768 - type = ((BoolExpr *) node)->boolop; -
9769 - parentType = ((BoolExpr *) parentNode)->boolop; -
9770 - switch (type) -
9771 - { -
9772 - case NOT_EXPR: -
9773 - case AND_EXPR: -
9774 - if (parentType == AND_EXPR || parentType == OR_EXPR) -
9775 - return true; -
9776 - break; -
9777 - case OR_EXPR: -
9778 - if (parentType == OR_EXPR) -
9779 - return true; -
9780 - break; -
9781 - } -
9782 - } -
9783 - return false; -
9784 - case T_FuncExpr: -
9785 - { -
9786 - /* special handling for casts and COERCE_SQL_SYNTAX */ -
9787 - CoercionForm type = ((FuncExpr *) parentNode)->funcformat; -
9788 - -
9789 - if (type == COERCE_EXPLICIT_CAST || -
9790 - type == COERCE_IMPLICIT_CAST || -
9791 - type == COERCE_SQL_SYNTAX) -
9792 - return false; -
9793 - return true; /* own parentheses */ -
9794 - } -
9795 - case T_SubscriptingRef: /* other separators */ -
9796 - case T_ArrayExpr: /* other separators */ -
9797 - case T_RowExpr: /* other separators */ -
9798 - case T_CoalesceExpr: /* own parentheses */ -
9799 - case T_MinMaxExpr: /* own parentheses */ -
9800 - case T_XmlExpr: /* own parentheses */ -
9801 - case T_NullIfExpr: /* other separators */ -
9802 - case T_Aggref: /* own parentheses */ -
9803 - case T_GroupingFunc: /* own parentheses */ -
9804 - case T_WindowFunc: /* own parentheses */ -
9805 - case T_CaseExpr: /* other separators */ -
9806 - case T_JsonExpr: /* own parentheses */ -
9807 - return true; -
9808 - default: -
9809 - return false; -
9810 - } -
9811 - -
9812 - case T_JsonValueExpr: -
9813 - /* maybe simple, check args */ -
9814 - return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr, -
9815 - node, prettyFlags); -
9816 - -
9817 - default: -
9818 - break; -
9819 - } -
9820 - /* those we don't know: in dubio complexo */ -
9821 - return false; -
9822 - } -
get_rule_expr() lines 10002-11477
Modified Lines Coverage: 38/40 lines (95.0%)
LineHitsSourceCommit
10002 - get_rule_expr(Node *node, deparse_context *context, -
10003 - bool showimplicit) -
10004 - { -
10005 - StringInfo buf = context->buf; -
10006 - -
10007 - if (node == NULL) -
10008 - return; -
10009 - -
10010 - /* Guard against excessively long or deeply-nested queries */ -
10011 - CHECK_FOR_INTERRUPTS(); -
10012 - check_stack_depth(); -
10013 - -
10014 - /* -
10015 - * Each level of get_rule_expr must emit an indivisible term -
10016 - * (parenthesized if necessary) to ensure result is reparsed into the same -
10017 - * expression tree. The only exception is that when the input is a List, -
10018 - * we emit the component items comma-separated with no surrounding -
10019 - * decoration; this is convenient for most callers. -
10020 - */ -
10021 - switch (nodeTag(node)) -
10022 - { -
10023 - case T_Var: -
10024 - (void) get_variable((Var *) node, 0, false, context); -
10025 - break; -
10026 - -
10027 - case T_Const: -
10028 - get_const_expr((Const *) node, context, 0); -
10029 - break; -
10030 - -
10031 - case T_Param: -
10032 - get_parameter((Param *) node, context); -
10033 - break; -
10034 - -
10035 - case T_Aggref: -
10036 - get_agg_expr((Aggref *) node, context, (Aggref *) node); -
10037 - break; -
10038 - -
10039 - case T_GroupingFunc: -
10040 - { -
10041 - GroupingFunc *gexpr = (GroupingFunc *) node; -
10042 - -
10043 - appendStringInfoString(buf, "GROUPING("); -
10044 - get_rule_expr((Node *) gexpr->args, context, true); -
10045 - appendStringInfoChar(buf, ')'); -
10046 - } -
10047 - break; -
10048 - -
10049 - case T_WindowFunc: -
10050 - get_windowfunc_expr((WindowFunc *) node, context); -
10051 - break; -
10052 - -
10053 - case T_MergeSupportFunc: -
10054 - appendStringInfoString(buf, "MERGE_ACTION()"); -
10055 - break; -
10056 - -
10057 - case T_SubscriptingRef: -
10058 - { -
10059 - SubscriptingRef *sbsref = (SubscriptingRef *) node; -
10060 - bool need_parens; -
10061 - -
10062 - /* -
10063 - * If the argument is a CaseTestExpr, we must be inside a -
10064 - * FieldStore, ie, we are assigning to an element of an array -
10065 - * within a composite column. Since we already punted on -
10066 - * displaying the FieldStore's target information, just punt -
10067 - * here too, and display only the assignment source -
10068 - * expression. -
10069 - */ -
10070 - if (IsA(sbsref->refexpr, CaseTestExpr)) -
10071 - { -
10072 - Assert(sbsref->refassgnexpr); -
10073 - get_rule_expr((Node *) sbsref->refassgnexpr, -
10074 - context, showimplicit); -
10075 - break; -
10076 - } -
10077 - -
10078 - /* -
10079 - * Parenthesize the argument unless it's a simple Var or a -
10080 - * FieldSelect. (In particular, if it's another -
10081 - * SubscriptingRef, we *must* parenthesize to avoid -
10082 - * confusion.) -
10083 - */ -
10084 - need_parens = !IsA(sbsref->refexpr, Var) && -
10085 - !IsA(sbsref->refexpr, FieldSelect); -
10086 - if (need_parens) -
10087 - appendStringInfoChar(buf, '('); -
10088 - get_rule_expr((Node *) sbsref->refexpr, context, showimplicit); -
10089 - if (need_parens) -
10090 - appendStringInfoChar(buf, ')'); -
10091 - -
10092 - /* -
10093 - * If there's a refassgnexpr, we want to print the node in the -
10094 - * format "container[subscripts] := refassgnexpr". This is -
10095 - * not legal SQL, so decompilation of INSERT or UPDATE -
10096 - * statements should always use processIndirection as part of -
10097 - * the statement-level syntax. We should only see this when -
10098 - * EXPLAIN tries to print the targetlist of a plan resulting -
10099 - * from such a statement. -
10100 - */ -
10101 - if (sbsref->refassgnexpr) -
10102 - { -
10103 - Node *refassgnexpr; -
10104 - -
10105 - /* -
10106 - * Use processIndirection to print this node's subscripts -
10107 - * as well as any additional field selections or -
10108 - * subscripting in immediate descendants. It returns the -
10109 - * RHS expr that is actually being "assigned". -
10110 - */ -
10111 - refassgnexpr = processIndirection(node, context); -
10112 - appendStringInfoString(buf, " := "); -
10113 - get_rule_expr(refassgnexpr, context, showimplicit); -
10114 - } -
10115 - else -
10116 - { -
10117 - /* Just an ordinary container fetch, so print subscripts */ -
10118 - printSubscripts(sbsref, context); -
10119 - } -
10120 - } -
10121 - break; -
10122 - -
10123 - case T_FuncExpr: -
10124 - get_func_expr((FuncExpr *) node, context, showimplicit); -
10125 - break; -
10126 - -
10127 280 case T_RPRNavExpr: 1f4b53eRow pattern recognition patch (rewriter).
10128 - { 1f4b53eRow pattern recognition patch (rewriter).
10129 280 RPRNavExpr *nav = (RPRNavExpr *) node; 1f4b53eRow pattern recognition patch (rewriter).
10130 280 const char *outer_func = NULL; 1f4b53eRow pattern recognition patch (rewriter).
10131 280 const char *inner_func; 1f4b53eRow pattern recognition patch (rewriter).
10132 - 1f4b53eRow pattern recognition patch (rewriter).
10133 280 switch (nav->kind) 1f4b53eRow pattern recognition patch (rewriter).
10134 - { 1f4b53eRow pattern recognition patch (rewriter).
10135 - case RPR_NAV_PREV: 1f4b53eRow pattern recognition patch (rewriter).
10136 - inner_func = "PREV("; 1f4b53eRow pattern recognition patch (rewriter).
10137 - break; 1f4b53eRow pattern recognition patch (rewriter).
10138 - case RPR_NAV_NEXT: 1f4b53eRow pattern recognition patch (rewriter).
10139 37 inner_func = "NEXT("; 1f4b53eRow pattern recognition patch (rewriter).
10140 37 break; 1f4b53eRow pattern recognition patch (rewriter).
10141 - case RPR_NAV_FIRST: 1f4b53eRow pattern recognition patch (rewriter).
10142 32 inner_func = "FIRST("; 1f4b53eRow pattern recognition patch (rewriter).
10143 32 break; 1f4b53eRow pattern recognition patch (rewriter).
10144 - case RPR_NAV_LAST: 1f4b53eRow pattern recognition patch (rewriter).
10145 23 inner_func = "LAST("; 1f4b53eRow pattern recognition patch (rewriter).
10146 23 break; 1f4b53eRow pattern recognition patch (rewriter).
10147 - case RPR_NAV_PREV_FIRST: 1f4b53eRow pattern recognition patch (rewriter).
10148 23 outer_func = "PREV("; 1f4b53eRow pattern recognition patch (rewriter).
10149 23 inner_func = "FIRST("; 1f4b53eRow pattern recognition patch (rewriter).
10150 23 break; 1f4b53eRow pattern recognition patch (rewriter).
10151 - case RPR_NAV_PREV_LAST: 1f4b53eRow pattern recognition patch (rewriter).
10152 9 outer_func = "PREV("; 1f4b53eRow pattern recognition patch (rewriter).
10153 9 inner_func = "LAST("; 1f4b53eRow pattern recognition patch (rewriter).
10154 9 break; 1f4b53eRow pattern recognition patch (rewriter).
10155 - case RPR_NAV_NEXT_FIRST: 1f4b53eRow pattern recognition patch (rewriter).
10156 9 outer_func = "NEXT("; 1f4b53eRow pattern recognition patch (rewriter).
10157 9 inner_func = "FIRST("; 1f4b53eRow pattern recognition patch (rewriter).
10158 9 break; 1f4b53eRow pattern recognition patch (rewriter).
10159 - case RPR_NAV_NEXT_LAST: 1f4b53eRow pattern recognition patch (rewriter).
10160 - outer_func = "NEXT("; 1f4b53eRow pattern recognition patch (rewriter).
10161 - inner_func = "LAST("; 1f4b53eRow pattern recognition patch (rewriter).
10162 - break; 1f4b53eRow pattern recognition patch (rewriter).
10163 0 default: 1f4b53eRow pattern recognition patch (rewriter).
UnreachableDefensive (unreachable) · confidence high · get_rule_expr @10163-10164 · 2 lines
Reason
Confirmed defensive-unreachable.
Lines 10163-10164 are the default: arm of the switch over RPRNavExpr->kind inside the T_RPRNavExpr case in get_rule_expr (ruleutils.c).
The RPRNavKind enum (primnodes.h:669-680) has exactly 8 members (RPR_NAV_PREV, NEXT, FIRST, LAST, PREV_FIRST, PREV_LAST, NEXT_FIRST, NEXT_LAST) and all 8 have explicit case labels at lines 10135-10162, so the default arm cannot match any in-range value.
The kind field is assigned during parse analysis from this fixed enum set;
no SQL syntax can produce an out-of-range value.
The WRITE_READ_PARSE_PLAN_TREES round-trip reads back the exact integer written, so it preserves the in-range value and cannot synthesize a bad kind either.
I tried to refute reachability via parser paths, optimizer rewrites, and copy/out/read node round-trips and found no input that drives an out-of-range kind into this deparse switch.
Line 10166 (inner_func = NULL "keep compiler quiet") is a dead store grouped with the same defensive arm.
This matches standard PostgreSQL switch-over-enum guard style used throughout ruleutils.c.
Recommended fix
Leave the elog(ERROR) default in place as the standard PostgreSQL defensive guard for switch-over-enum;
it is consistent with neighboring node-deparse code in ruleutils.c and protects against future RPRNavKind additions.
No source change recommended.
If maximizing measured coverage were a hard requirement, the only options would be a coverage-exclusion marker or dropping the default to rely on -Wswitch exhaustiveness, neither of which is advisable.
10164 0 elog(ERROR, "unrecognized RPR navigation kind: %d", 1f4b53eRow pattern recognition patch (rewriter).
10165 - nav->kind); 1f4b53eRow pattern recognition patch (rewriter).
10166 - inner_func = NULL; /* keep compiler quiet */ 1f4b53eRow pattern recognition patch (rewriter).
10167 - break; 1f4b53eRow pattern recognition patch (rewriter).
10168 - } 1f4b53eRow pattern recognition patch (rewriter).
10169 - 1f4b53eRow pattern recognition patch (rewriter).
10170 133 if (outer_func != NULL) 1f4b53eRow pattern recognition patch (rewriter).
10171 - { 1f4b53eRow pattern recognition patch (rewriter).
10172 - /* 1f4b53eRow pattern recognition patch (rewriter).
10173 - * Compound: PREV(FIRST(arg [, inner_offset]) [, 1f4b53eRow pattern recognition patch (rewriter).
10174 - * outer_offset]) 1f4b53eRow pattern recognition patch (rewriter).
10175 - */ 1f4b53eRow pattern recognition patch (rewriter).
10176 50 appendStringInfoString(buf, outer_func); 1f4b53eRow pattern recognition patch (rewriter).
10177 50 appendStringInfoString(buf, inner_func); 1f4b53eRow pattern recognition patch (rewriter).
10178 50 get_rule_expr((Node *) nav->arg, context, showimplicit); 1f4b53eRow pattern recognition patch (rewriter).
10179 50 if (nav->offset_arg != NULL) 1f4b53eRow pattern recognition patch (rewriter).
10180 - { 1f4b53eRow pattern recognition patch (rewriter).
10181 18 appendStringInfoString(buf, ", "); 1f4b53eRow pattern recognition patch (rewriter).
10182 18 get_rule_expr((Node *) nav->offset_arg, context, 1f4b53eRow pattern recognition patch (rewriter).
10183 - showimplicit); 1f4b53eRow pattern recognition patch (rewriter).
10184 - } 1f4b53eRow pattern recognition patch (rewriter).
10185 50 appendStringInfoChar(buf, ')'); 1f4b53eRow pattern recognition patch (rewriter).
10186 50 if (nav->compound_offset_arg != NULL) 1f4b53eRow pattern recognition patch (rewriter).
10187 - { 1f4b53eRow pattern recognition patch (rewriter).
10188 41 appendStringInfoString(buf, ", "); 1f4b53eRow pattern recognition patch (rewriter).
10189 41 get_rule_expr((Node *) nav->compound_offset_arg, 1f4b53eRow pattern recognition patch (rewriter).
10190 - context, showimplicit); 1f4b53eRow pattern recognition patch (rewriter).
10191 - } 1f4b53eRow pattern recognition patch (rewriter).
10192 50 appendStringInfoChar(buf, ')'); 1f4b53eRow pattern recognition patch (rewriter).
10193 - } 1f4b53eRow pattern recognition patch (rewriter).
10194 - else 1f4b53eRow pattern recognition patch (rewriter).
10195 - { 1f4b53eRow pattern recognition patch (rewriter).
10196 - /* Simple: FUNC(arg [, offset]) */ 1f4b53eRow pattern recognition patch (rewriter).
10197 230 appendStringInfoString(buf, inner_func); 1f4b53eRow pattern recognition patch (rewriter).
10198 230 get_rule_expr((Node *) nav->arg, context, showimplicit); 1f4b53eRow pattern recognition patch (rewriter).
10199 230 if (nav->offset_arg != NULL) 1f4b53eRow pattern recognition patch (rewriter).
10200 - { 1f4b53eRow pattern recognition patch (rewriter).
10201 46 appendStringInfoString(buf, ", "); 1f4b53eRow pattern recognition patch (rewriter).
10202 46 get_rule_expr((Node *) nav->offset_arg, context, 1f4b53eRow pattern recognition patch (rewriter).
10203 - showimplicit); 1f4b53eRow pattern recognition patch (rewriter).
10204 - } 1f4b53eRow pattern recognition patch (rewriter).
10205 230 appendStringInfoChar(buf, ')'); 1f4b53eRow pattern recognition patch (rewriter).
10206 - } 1f4b53eRow pattern recognition patch (rewriter).
10207 - } 1f4b53eRow pattern recognition patch (rewriter).
10208 - break; 1f4b53eRow pattern recognition patch (rewriter).
10209 - 1f4b53eRow pattern recognition patch (rewriter).
10210 - case T_NamedArgExpr: -
10211 - { -
10212 - NamedArgExpr *na = (NamedArgExpr *) node; -
10213 - -
10214 - appendStringInfo(buf, "%s => ", quote_identifier(na->name)); -
10215 - get_rule_expr((Node *) na->arg, context, showimplicit); -
10216 - } -
10217 - break; -
10218 - -
10219 - case T_OpExpr: -
10220 - get_oper_expr((OpExpr *) node, context); -
10221 - break; -
10222 - -
10223 - case T_DistinctExpr: -
10224 - { -
10225 - DistinctExpr *expr = (DistinctExpr *) node; -
10226 - List *args = expr->args; -
10227 - Node *arg1 = (Node *) linitial(args); -
10228 - Node *arg2 = (Node *) lsecond(args); -
10229 - -
10230 - if (!PRETTY_PAREN(context)) -
10231 - appendStringInfoChar(buf, '('); -
10232 - get_rule_expr_paren(arg1, context, true, node); -
10233 - appendStringInfoString(buf, " IS DISTINCT FROM "); -
10234 - get_rule_expr_paren(arg2, context, true, node); -
10235 - if (!PRETTY_PAREN(context)) -
10236 - appendStringInfoChar(buf, ')'); -
10237 - } -
10238 - break; -
10239 - -
10240 - case T_NullIfExpr: -
10241 - { -
10242 - NullIfExpr *nullifexpr = (NullIfExpr *) node; -
10243 - -
10244 - appendStringInfoString(buf, "NULLIF("); -
10245 - get_rule_expr((Node *) nullifexpr->args, context, true); -
10246 - appendStringInfoChar(buf, ')'); -
10247 - } -
10248 - break; -
10249 - -
10250 - case T_ScalarArrayOpExpr: -
10251 - { -
10252 - ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; -
10253 - List *args = expr->args; -
10254 - Node *arg1 = (Node *) linitial(args); -
10255 - Node *arg2 = (Node *) lsecond(args); -
10256 - -
10257 - if (!PRETTY_PAREN(context)) -
10258 - appendStringInfoChar(buf, '('); -
10259 - get_rule_expr_paren(arg1, context, true, node); -
10260 - appendStringInfo(buf, " %s %s (", -
10261 - generate_operator_name(expr->opno, -
10262 - exprType(arg1), -
10263 - get_base_element_type(exprType(arg2))), -
10264 - expr->useOr ? "ANY" : "ALL"); -
10265 - get_rule_expr_paren(arg2, context, true, node); -
10266 - -
10267 - /* -
10268 - * There's inherent ambiguity in "x op ANY/ALL (y)" when y is -
10269 - * a bare sub-SELECT. Since we're here, the sub-SELECT must -
10270 - * be meant as a scalar sub-SELECT yielding an array value to -
10271 - * be used in ScalarArrayOpExpr; but the grammar will -
10272 - * preferentially interpret such a construct as an ANY/ALL -
10273 - * SubLink. To prevent misparsing the output that way, insert -
10274 - * a dummy coercion (which will be stripped by parse analysis, -
10275 - * so no inefficiency is added in dump and reload). This is -
10276 - * indeed most likely what the user wrote to get the construct -
10277 - * accepted in the first place. -
10278 - */ -
10279 - if (IsA(arg2, SubLink) && -
10280 - ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK) -
10281 - appendStringInfo(buf, "::%s", -
10282 - format_type_with_typemod(exprType(arg2), -
10283 - exprTypmod(arg2))); -
10284 - appendStringInfoChar(buf, ')'); -
10285 - if (!PRETTY_PAREN(context)) -
10286 - appendStringInfoChar(buf, ')'); -
10287 - } -
10288 - break; -
10289 - -
10290 - case T_BoolExpr: -
10291 - { -
10292 - BoolExpr *expr = (BoolExpr *) node; -
10293 - Node *first_arg = linitial(expr->args); -
10294 - ListCell *arg; -
10295 - -
10296 - switch (expr->boolop) -
10297 - { -
10298 - case AND_EXPR: -
10299 - if (!PRETTY_PAREN(context)) -
10300 - appendStringInfoChar(buf, '('); -
10301 - get_rule_expr_paren(first_arg, context, -
10302 - false, node); -
10303 - for_each_from(arg, expr->args, 1) -
10304 - { -
10305 - appendStringInfoString(buf, " AND "); -
10306 - get_rule_expr_paren((Node *) lfirst(arg), context, -
10307 - false, node); -
10308 - } -
10309 - if (!PRETTY_PAREN(context)) -
10310 - appendStringInfoChar(buf, ')'); -
10311 - break; -
10312 - -
10313 - case OR_EXPR: -
10314 - if (!PRETTY_PAREN(context)) -
10315 - appendStringInfoChar(buf, '('); -
10316 - get_rule_expr_paren(first_arg, context, -
10317 - false, node); -
10318 - for_each_from(arg, expr->args, 1) -
10319 - { -
10320 - appendStringInfoString(buf, " OR "); -
10321 - get_rule_expr_paren((Node *) lfirst(arg), context, -
10322 - false, node); -
10323 - } -
10324 - if (!PRETTY_PAREN(context)) -
10325 - appendStringInfoChar(buf, ')'); -
10326 - break; -
10327 - -
10328 - case NOT_EXPR: -
10329 - if (!PRETTY_PAREN(context)) -
10330 - appendStringInfoChar(buf, '('); -
10331 - appendStringInfoString(buf, "NOT "); -
10332 - get_rule_expr_paren(first_arg, context, -
10333 - false, node); -
10334 - if (!PRETTY_PAREN(context)) -
10335 - appendStringInfoChar(buf, ')'); -
10336 - break; -
10337 - -
10338 - default: -
10339 - elog(ERROR, "unrecognized boolop: %d", -
10340 - (int) expr->boolop); -
10341 - } -
10342 - } -
10343 - break; -
10344 - -
10345 - case T_SubLink: -
10346 - get_sublink_expr((SubLink *) node, context); -
10347 - break; -
10348 - -
10349 - case T_SubPlan: -
10350 - { -
10351 - SubPlan *subplan = (SubPlan *) node; -
10352 - -
10353 - /* -
10354 - * We cannot see an already-planned subplan in rule deparsing, -
10355 - * only while EXPLAINing a query plan. We don't try to -
10356 - * reconstruct the original SQL, just reference the subplan -
10357 - * that appears elsewhere in EXPLAIN's result. It does seem -
10358 - * useful to show the subLinkType and testexpr (if any), and -
10359 - * we also note whether the subplan will be hashed. -
10360 - */ -
10361 - switch (subplan->subLinkType) -
10362 - { -
10363 - case EXISTS_SUBLINK: -
10364 - appendStringInfoString(buf, "EXISTS("); -
10365 - Assert(subplan->testexpr == NULL); -
10366 - break; -
10367 - case ALL_SUBLINK: -
10368 - appendStringInfoString(buf, "(ALL "); -
10369 - Assert(subplan->testexpr != NULL); -
10370 - break; -
10371 - case ANY_SUBLINK: -
10372 - appendStringInfoString(buf, "(ANY "); -
10373 - Assert(subplan->testexpr != NULL); -
10374 - break; -
10375 - case ROWCOMPARE_SUBLINK: -
10376 - /* Parenthesizing the testexpr seems sufficient */ -
10377 - appendStringInfoChar(buf, '('); -
10378 - Assert(subplan->testexpr != NULL); -
10379 - break; -
10380 - case EXPR_SUBLINK: -
10381 - /* No need to decorate these subplan references */ -
10382 - appendStringInfoChar(buf, '('); -
10383 - Assert(subplan->testexpr == NULL); -
10384 - break; -
10385 - case MULTIEXPR_SUBLINK: -
10386 - /* MULTIEXPR isn't executed in the normal way */ -
10387 - appendStringInfoString(buf, "(rescan "); -
10388 - Assert(subplan->testexpr == NULL); -
10389 - break; -
10390 - case ARRAY_SUBLINK: -
10391 - appendStringInfoString(buf, "ARRAY("); -
10392 - Assert(subplan->testexpr == NULL); -
10393 - break; -
10394 - case CTE_SUBLINK: -
10395 - /* This case is unreachable within expressions */ -
10396 - appendStringInfoString(buf, "CTE("); -
10397 - Assert(subplan->testexpr == NULL); -
10398 - break; -
10399 - } -
10400 - -
10401 - if (subplan->testexpr != NULL) -
10402 - { -
10403 - deparse_namespace *dpns; -
10404 - -
10405 - /* -
10406 - * Push SubPlan into ancestors list while deparsing -
10407 - * testexpr, so that we can handle PARAM_EXEC references -
10408 - * to the SubPlan's paramIds. (This makes it look like -
10409 - * the SubPlan is an "ancestor" of the current plan node, -
10410 - * which is a little weird, but it does no harm.) In this -
10411 - * path, we don't need to mention the SubPlan explicitly, -
10412 - * because the referencing Params will show its existence. -
10413 - */ -
10414 - dpns = (deparse_namespace *) linitial(context->namespaces); -
10415 - dpns->ancestors = lcons(subplan, dpns->ancestors); -
10416 - -
10417 - get_rule_expr(subplan->testexpr, context, showimplicit); -
10418 - appendStringInfoChar(buf, ')'); -
10419 - -
10420 - dpns->ancestors = list_delete_first(dpns->ancestors); -
10421 - } -
10422 - else -
10423 - { -
10424 - const char *nameprefix; -
10425 - -
10426 - /* No referencing Params, so show the SubPlan's name */ -
10427 - if (subplan->isInitPlan) -
10428 - nameprefix = "InitPlan "; -
10429 - else -
10430 - nameprefix = "SubPlan "; -
10431 - if (subplan->useHashTable) -
10432 - appendStringInfo(buf, "hashed %s%s)", -
10433 - nameprefix, subplan->plan_name); -
10434 - else -
10435 - appendStringInfo(buf, "%s%s)", -
10436 - nameprefix, subplan->plan_name); -
10437 - } -
10438 - } -
10439 - break; -
10440 - -
10441 - case T_AlternativeSubPlan: -
10442 - { -
10443 - AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; -
10444 - ListCell *lc; -
10445 - -
10446 - /* -
10447 - * This case cannot be reached in normal usage, since no -
10448 - * AlternativeSubPlan can appear either in parsetrees or -
10449 - * finished plan trees. We keep it just in case somebody -
10450 - * wants to use this code to print planner data structures. -
10451 - */ -
10452 - appendStringInfoString(buf, "(alternatives: "); -
10453 - foreach(lc, asplan->subplans) -
10454 - { -
10455 - SubPlan *splan = lfirst_node(SubPlan, lc); -
10456 - const char *nameprefix; -
10457 - -
10458 - if (splan->isInitPlan) -
10459 - nameprefix = "InitPlan "; -
10460 - else -
10461 - nameprefix = "SubPlan "; -
10462 - if (splan->useHashTable) -
10463 - appendStringInfo(buf, "hashed %s%s", nameprefix, -
10464 - splan->plan_name); -
10465 - else -
10466 - appendStringInfo(buf, "%s%s", nameprefix, -
10467 - splan->plan_name); -
10468 - if (lnext(asplan->subplans, lc)) -
10469 - appendStringInfoString(buf, " or "); -
10470 - } -
10471 - appendStringInfoChar(buf, ')'); -
10472 - } -
10473 - break; -
10474 - -
10475 - case T_FieldSelect: -
10476 - { -
10477 - FieldSelect *fselect = (FieldSelect *) node; -
10478 - Node *arg = (Node *) fselect->arg; -
10479 - int fno = fselect->fieldnum; -
10480 - const char *fieldname; -
10481 - bool need_parens; -
10482 - -
10483 - /* -
10484 - * Parenthesize the argument unless it's a SubscriptingRef or -
10485 - * another FieldSelect. Note in particular that it would be -
10486 - * WRONG to not parenthesize a Var argument; simplicity is not -
10487 - * the issue here, having the right number of names is. -
10488 - */ -
10489 - need_parens = !IsA(arg, SubscriptingRef) && -
10490 - !IsA(arg, FieldSelect); -
10491 - if (need_parens) -
10492 - appendStringInfoChar(buf, '('); -
10493 - get_rule_expr(arg, context, true); -
10494 - if (need_parens) -
10495 - appendStringInfoChar(buf, ')'); -
10496 - -
10497 - /* -
10498 - * Get and print the field name. -
10499 - */ -
10500 - fieldname = get_name_for_var_field((Var *) arg, fno, -
10501 - 0, context); -
10502 - appendStringInfo(buf, ".%s", quote_identifier(fieldname)); -
10503 - } -
10504 - break; -
10505 - -
10506 - case T_FieldStore: -
10507 - { -
10508 - FieldStore *fstore = (FieldStore *) node; -
10509 - bool need_parens; -
10510 - -
10511 - /* -
10512 - * There is no good way to represent a FieldStore as real SQL, -
10513 - * so decompilation of INSERT or UPDATE statements should -
10514 - * always use processIndirection as part of the -
10515 - * statement-level syntax. We should only get here when -
10516 - * EXPLAIN tries to print the targetlist of a plan resulting -
10517 - * from such a statement. The plan case is even harder than -
10518 - * ordinary rules would be, because the planner tries to -
10519 - * collapse multiple assignments to the same field or subfield -
10520 - * into one FieldStore; so we can see a list of target fields -
10521 - * not just one, and the arguments could be FieldStores -
10522 - * themselves. We don't bother to try to print the target -
10523 - * field names; we just print the source arguments, with a -
10524 - * ROW() around them if there's more than one. This isn't -
10525 - * terribly complete, but it's probably good enough for -
10526 - * EXPLAIN's purposes; especially since anything more would be -
10527 - * either hopelessly confusing or an even poorer -
10528 - * representation of what the plan is actually doing. -
10529 - */ -
10530 - need_parens = (list_length(fstore->newvals) != 1); -
10531 - if (need_parens) -
10532 - appendStringInfoString(buf, "ROW("); -
10533 - get_rule_expr((Node *) fstore->newvals, context, showimplicit); -
10534 - if (need_parens) -
10535 - appendStringInfoChar(buf, ')'); -
10536 - } -
10537 - break; -
10538 - -
10539 - case T_RelabelType: -
10540 - { -
10541 - RelabelType *relabel = (RelabelType *) node; -
10542 - Node *arg = (Node *) relabel->arg; -
10543 - -
10544 - if (relabel->relabelformat == COERCE_IMPLICIT_CAST && -
10545 - !showimplicit) -
10546 - { -
10547 - /* don't show the implicit cast */ -
10548 - get_rule_expr_paren(arg, context, false, node); -
10549 - } -
10550 - else -
10551 - { -
10552 - get_coercion_expr(arg, context, -
10553 - relabel->resulttype, -
10554 - relabel->resulttypmod, -
10555 - node); -
10556 - } -
10557 - } -
10558 - break; -
10559 - -
10560 - case T_CoerceViaIO: -
10561 - { -
10562 - CoerceViaIO *iocoerce = (CoerceViaIO *) node; -
10563 - Node *arg = (Node *) iocoerce->arg; -
10564 - -
10565 - if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST && -
10566 - !showimplicit) -
10567 - { -
10568 - /* don't show the implicit cast */ -
10569 - get_rule_expr_paren(arg, context, false, node); -
10570 - } -
10571 - else -
10572 - { -
10573 - get_coercion_expr(arg, context, -
10574 - iocoerce->resulttype, -
10575 - -1, -
10576 - node); -
10577 - } -
10578 - } -
10579 - break; -
10580 - -
10581 - case T_ArrayCoerceExpr: -
10582 - { -
10583 - ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; -
10584 - Node *arg = (Node *) acoerce->arg; -
10585 - -
10586 - if (acoerce->coerceformat == COERCE_IMPLICIT_CAST && -
10587 - !showimplicit) -
10588 - { -
10589 - /* don't show the implicit cast */ -
10590 - get_rule_expr_paren(arg, context, false, node); -
10591 - } -
10592 - else -
10593 - { -
10594 - get_coercion_expr(arg, context, -
10595 - acoerce->resulttype, -
10596 - acoerce->resulttypmod, -
10597 - node); -
10598 - } -
10599 - } -
10600 - break; -
10601 - -
10602 - case T_ConvertRowtypeExpr: -
10603 - { -
10604 - ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; -
10605 - Node *arg = (Node *) convert->arg; -
10606 - -
10607 - if (convert->convertformat == COERCE_IMPLICIT_CAST && -
10608 - !showimplicit) -
10609 - { -
10610 - /* don't show the implicit cast */ -
10611 - get_rule_expr_paren(arg, context, false, node); -
10612 - } -
10613 - else -
10614 - { -
10615 - get_coercion_expr(arg, context, -
10616 - convert->resulttype, -1, -
10617 - node); -
10618 - } -
10619 - } -
10620 - break; -
10621 - -
10622 - case T_CollateExpr: -
10623 - { -
10624 - CollateExpr *collate = (CollateExpr *) node; -
10625 - Node *arg = (Node *) collate->arg; -
10626 - -
10627 - if (!PRETTY_PAREN(context)) -
10628 - appendStringInfoChar(buf, '('); -
10629 - get_rule_expr_paren(arg, context, showimplicit, node); -
10630 - appendStringInfo(buf, " COLLATE %s", -
10631 - generate_collation_name(collate->collOid)); -
10632 - if (!PRETTY_PAREN(context)) -
10633 - appendStringInfoChar(buf, ')'); -
10634 - } -
10635 - break; -
10636 - -
10637 - case T_CaseExpr: -
10638 - { -
10639 - CaseExpr *caseexpr = (CaseExpr *) node; -
10640 - ListCell *temp; -
10641 - -
10642 - appendContextKeyword(context, "CASE", -
10643 - 0, PRETTYINDENT_VAR, 0); -
10644 - if (caseexpr->arg) -
10645 - { -
10646 - appendStringInfoChar(buf, ' '); -
10647 - get_rule_expr((Node *) caseexpr->arg, context, true); -
10648 - } -
10649 - foreach(temp, caseexpr->args) -
10650 - { -
10651 - CaseWhen *when = (CaseWhen *) lfirst(temp); -
10652 - Node *w = (Node *) when->expr; -
10653 - -
10654 - if (caseexpr->arg) -
10655 - { -
10656 - /* -
10657 - * The parser should have produced WHEN clauses of the -
10658 - * form "CaseTestExpr = RHS", possibly with an -
10659 - * implicit coercion inserted above the CaseTestExpr. -
10660 - * For accurate decompilation of rules it's essential -
10661 - * that we show just the RHS. However in an -
10662 - * expression that's been through the optimizer, the -
10663 - * WHEN clause could be almost anything (since the -
10664 - * equality operator could have been expanded into an -
10665 - * inline function). If we don't recognize the form -
10666 - * of the WHEN clause, just punt and display it as-is. -
10667 - */ -
10668 - if (IsA(w, OpExpr)) -
10669 - { -
10670 - List *args = ((OpExpr *) w)->args; -
10671 - -
10672 - if (list_length(args) == 2 && -
10673 - IsA(strip_implicit_coercions(linitial(args)), -
10674 - CaseTestExpr)) -
10675 - w = (Node *) lsecond(args); -
10676 - } -
10677 - } -
10678 - -
10679 - if (!PRETTY_INDENT(context)) -
10680 - appendStringInfoChar(buf, ' '); -
10681 - appendContextKeyword(context, "WHEN ", -
10682 - 0, 0, 0); -
10683 - get_rule_expr(w, context, false); -
10684 - appendStringInfoString(buf, " THEN "); -
10685 - get_rule_expr((Node *) when->result, context, true); -
10686 - } -
10687 - if (!PRETTY_INDENT(context)) -
10688 - appendStringInfoChar(buf, ' '); -
10689 - appendContextKeyword(context, "ELSE ", -
10690 - 0, 0, 0); -
10691 - get_rule_expr((Node *) caseexpr->defresult, context, true); -
10692 - if (!PRETTY_INDENT(context)) -
10693 - appendStringInfoChar(buf, ' '); -
10694 - appendContextKeyword(context, "END", -
10695 - -PRETTYINDENT_VAR, 0, 0); -
10696 - } -
10697 - break; -
10698 - -
10699 - case T_CaseTestExpr: -
10700 - { -
10701 - /* -
10702 - * Normally we should never get here, since for expressions -
10703 - * that can contain this node type we attempt to avoid -
10704 - * recursing to it. But in an optimized expression we might -
10705 - * be unable to avoid that (see comments for CaseExpr). If we -
10706 - * do see one, print it as CASE_TEST_EXPR. -
10707 - */ -
10708 - appendStringInfoString(buf, "CASE_TEST_EXPR"); -
10709 - } -
10710 - break; -
10711 - -
10712 - case T_ArrayExpr: -
10713 - { -
10714 - ArrayExpr *arrayexpr = (ArrayExpr *) node; -
10715 - -
10716 - appendStringInfoString(buf, "ARRAY["); -
10717 - get_rule_expr((Node *) arrayexpr->elements, context, true); -
10718 - appendStringInfoChar(buf, ']'); -
10719 - -
10720 - /* -
10721 - * If the array isn't empty, we assume its elements are -
10722 - * coerced to the desired type. If it's empty, though, we -
10723 - * need an explicit coercion to the array type. -
10724 - */ -
10725 - if (arrayexpr->elements == NIL) -
10726 - appendStringInfo(buf, "::%s", -
10727 - format_type_with_typemod(arrayexpr->array_typeid, -1)); -
10728 - } -
10729 - break; -
10730 - -
10731 - case T_RowExpr: -
10732 - { -
10733 - RowExpr *rowexpr = (RowExpr *) node; -
10734 - TupleDesc tupdesc = NULL; -
10735 - ListCell *arg; -
10736 - int i; -
10737 - char *sep; -
10738 - -
10739 - /* -
10740 - * If it's a named type and not RECORD, we may have to skip -
10741 - * dropped columns and/or claim there are NULLs for added -
10742 - * columns. -
10743 - */ -
10744 - if (rowexpr->row_typeid != RECORDOID) -
10745 - { -
10746 - tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); -
10747 - Assert(list_length(rowexpr->args) <= tupdesc->natts); -
10748 - } -
10749 - -
10750 - /* -
10751 - * SQL99 allows "ROW" to be omitted when there is more than -
10752 - * one column, but for simplicity we always print it. -
10753 - */ -
10754 - appendStringInfoString(buf, "ROW("); -
10755 - sep = ""; -
10756 - i = 0; -
10757 - foreach(arg, rowexpr->args) -
10758 - { -
10759 - Node *e = (Node *) lfirst(arg); -
10760 - -
10761 - if (tupdesc == NULL || -
10762 - !TupleDescCompactAttr(tupdesc, i)->attisdropped) -
10763 - { -
10764 - appendStringInfoString(buf, sep); -
10765 - /* Whole-row Vars need special treatment here */ -
10766 - get_rule_expr_toplevel(e, context, true); -
10767 - sep = ", "; -
10768 - } -
10769 - i++; -
10770 - } -
10771 - if (tupdesc != NULL) -
10772 - { -
10773 - while (i < tupdesc->natts) -
10774 - { -
10775 - if (!TupleDescCompactAttr(tupdesc, i)->attisdropped) -
10776 - { -
10777 - appendStringInfoString(buf, sep); -
10778 - appendStringInfoString(buf, "NULL"); -
10779 - sep = ", "; -
10780 - } -
10781 - i++; -
10782 - } -
10783 - -
10784 - ReleaseTupleDesc(tupdesc); -
10785 - } -
10786 - appendStringInfoChar(buf, ')'); -
10787 - if (rowexpr->row_format == COERCE_EXPLICIT_CAST) -
10788 - appendStringInfo(buf, "::%s", -
10789 - format_type_with_typemod(rowexpr->row_typeid, -1)); -
10790 - } -
10791 - break; -
10792 - -
10793 - case T_RowCompareExpr: -
10794 - { -
10795 - RowCompareExpr *rcexpr = (RowCompareExpr *) node; -
10796 - -
10797 - /* -
10798 - * SQL99 allows "ROW" to be omitted when there is more than -
10799 - * one column, but for simplicity we always print it. Within -
10800 - * a ROW expression, whole-row Vars need special treatment, so -
10801 - * use get_rule_list_toplevel. -
10802 - */ -
10803 - appendStringInfoString(buf, "(ROW("); -
10804 - get_rule_list_toplevel(rcexpr->largs, context, true); -
10805 - -
10806 - /* -
10807 - * We assume that the name of the first-column operator will -
10808 - * do for all the rest too. This is definitely open to -
10809 - * failure, eg if some but not all operators were renamed -
10810 - * since the construct was parsed, but there seems no way to -
10811 - * be perfect. -
10812 - */ -
10813 - appendStringInfo(buf, ") %s ROW(", -
10814 - generate_operator_name(linitial_oid(rcexpr->opnos), -
10815 - exprType(linitial(rcexpr->largs)), -
10816 - exprType(linitial(rcexpr->rargs)))); -
10817 - get_rule_list_toplevel(rcexpr->rargs, context, true); -
10818 - appendStringInfoString(buf, "))"); -
10819 - } -
10820 - break; -
10821 - -
10822 - case T_CoalesceExpr: -
10823 - { -
10824 - CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; -
10825 - -
10826 - appendStringInfoString(buf, "COALESCE("); -
10827 - get_rule_expr((Node *) coalesceexpr->args, context, true); -
10828 - appendStringInfoChar(buf, ')'); -
10829 - } -
10830 - break; -
10831 - -
10832 - case T_MinMaxExpr: -
10833 - { -
10834 - MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; -
10835 - -
10836 - switch (minmaxexpr->op) -
10837 - { -
10838 - case IS_GREATEST: -
10839 - appendStringInfoString(buf, "GREATEST("); -
10840 - break; -
10841 - case IS_LEAST: -
10842 - appendStringInfoString(buf, "LEAST("); -
10843 - break; -
10844 - } -
10845 - get_rule_expr((Node *) minmaxexpr->args, context, true); -
10846 - appendStringInfoChar(buf, ')'); -
10847 - } -
10848 - break; -
10849 - -
10850 - case T_SQLValueFunction: -
10851 - { -
10852 - SQLValueFunction *svf = (SQLValueFunction *) node; -
10853 - -
10854 - /* -
10855 - * Note: this code knows that typmod for time, timestamp, and -
10856 - * timestamptz just prints as integer. -
10857 - */ -
10858 - switch (svf->op) -
10859 - { -
10860 - case SVFOP_CURRENT_DATE: -
10861 - appendStringInfoString(buf, "CURRENT_DATE"); -
10862 - break; -
10863 - case SVFOP_CURRENT_TIME: -
10864 - appendStringInfoString(buf, "CURRENT_TIME"); -
10865 - break; -
10866 - case SVFOP_CURRENT_TIME_N: -
10867 - appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod); -
10868 - break; -
10869 - case SVFOP_CURRENT_TIMESTAMP: -
10870 - appendStringInfoString(buf, "CURRENT_TIMESTAMP"); -
10871 - break; -
10872 - case SVFOP_CURRENT_TIMESTAMP_N: -
10873 - appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)", -
10874 - svf->typmod); -
10875 - break; -
10876 - case SVFOP_LOCALTIME: -
10877 - appendStringInfoString(buf, "LOCALTIME"); -
10878 - break; -
10879 - case SVFOP_LOCALTIME_N: -
10880 - appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod); -
10881 - break; -
10882 - case SVFOP_LOCALTIMESTAMP: -
10883 - appendStringInfoString(buf, "LOCALTIMESTAMP"); -
10884 - break; -
10885 - case SVFOP_LOCALTIMESTAMP_N: -
10886 - appendStringInfo(buf, "LOCALTIMESTAMP(%d)", -
10887 - svf->typmod); -
10888 - break; -
10889 - case SVFOP_CURRENT_ROLE: -
10890 - appendStringInfoString(buf, "CURRENT_ROLE"); -
10891 - break; -
10892 - case SVFOP_CURRENT_USER: -
10893 - appendStringInfoString(buf, "CURRENT_USER"); -
10894 - break; -
10895 - case SVFOP_USER: -
10896 - appendStringInfoString(buf, "USER"); -
10897 - break; -
10898 - case SVFOP_SESSION_USER: -
10899 - appendStringInfoString(buf, "SESSION_USER"); -
10900 - break; -
10901 - case SVFOP_CURRENT_CATALOG: -
10902 - appendStringInfoString(buf, "CURRENT_CATALOG"); -
10903 - break; -
10904 - case SVFOP_CURRENT_SCHEMA: -
10905 - appendStringInfoString(buf, "CURRENT_SCHEMA"); -
10906 - break; -
10907 - } -
10908 - } -
10909 - break; -
10910 - -
10911 - case T_XmlExpr: -
10912 - { -
10913 - XmlExpr *xexpr = (XmlExpr *) node; -
10914 - bool needcomma = false; -
10915 - ListCell *arg; -
10916 - ListCell *narg; -
10917 - Const *con; -
10918 - -
10919 - switch (xexpr->op) -
10920 - { -
10921 - case IS_XMLCONCAT: -
10922 - appendStringInfoString(buf, "XMLCONCAT("); -
10923 - break; -
10924 - case IS_XMLELEMENT: -
10925 - appendStringInfoString(buf, "XMLELEMENT("); -
10926 - break; -
10927 - case IS_XMLFOREST: -
10928 - appendStringInfoString(buf, "XMLFOREST("); -
10929 - break; -
10930 - case IS_XMLPARSE: -
10931 - appendStringInfoString(buf, "XMLPARSE("); -
10932 - break; -
10933 - case IS_XMLPI: -
10934 - appendStringInfoString(buf, "XMLPI("); -
10935 - break; -
10936 - case IS_XMLROOT: -
10937 - appendStringInfoString(buf, "XMLROOT("); -
10938 - break; -
10939 - case IS_XMLSERIALIZE: -
10940 - appendStringInfoString(buf, "XMLSERIALIZE("); -
10941 - break; -
10942 - case IS_DOCUMENT: -
10943 - break; -
10944 - } -
10945 - if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) -
10946 - { -
10947 - if (xexpr->xmloption == XMLOPTION_DOCUMENT) -
10948 - appendStringInfoString(buf, "DOCUMENT "); -
10949 - else -
10950 - appendStringInfoString(buf, "CONTENT "); -
10951 - } -
10952 - if (xexpr->name) -
10953 - { -
10954 - appendStringInfo(buf, "NAME %s", -
10955 - quote_identifier(map_xml_name_to_sql_identifier(xexpr->name))); -
10956 - needcomma = true; -
10957 - } -
10958 - if (xexpr->named_args) -
10959 - { -
10960 - if (xexpr->op != IS_XMLFOREST) -
10961 - { -
10962 - if (needcomma) -
10963 - appendStringInfoString(buf, ", "); -
10964 - appendStringInfoString(buf, "XMLATTRIBUTES("); -
10965 - needcomma = false; -
10966 - } -
10967 - forboth(arg, xexpr->named_args, narg, xexpr->arg_names) -
10968 - { -
10969 - Node *e = (Node *) lfirst(arg); -
10970 - char *argname = strVal(lfirst(narg)); -
10971 - -
10972 - if (needcomma) -
10973 - appendStringInfoString(buf, ", "); -
10974 - get_rule_expr(e, context, true); -
10975 - appendStringInfo(buf, " AS %s", -
10976 - quote_identifier(map_xml_name_to_sql_identifier(argname))); -
10977 - needcomma = true; -
10978 - } -
10979 - if (xexpr->op != IS_XMLFOREST) -
10980 - appendStringInfoChar(buf, ')'); -
10981 - } -
10982 - if (xexpr->args) -
10983 - { -
10984 - if (needcomma) -
10985 - appendStringInfoString(buf, ", "); -
10986 - switch (xexpr->op) -
10987 - { -
10988 - case IS_XMLCONCAT: -
10989 - case IS_XMLELEMENT: -
10990 - case IS_XMLFOREST: -
10991 - case IS_XMLPI: -
10992 - case IS_XMLSERIALIZE: -
10993 - /* no extra decoration needed */ -
10994 - get_rule_expr((Node *) xexpr->args, context, true); -
10995 - break; -
10996 - case IS_XMLPARSE: -
10997 - Assert(list_length(xexpr->args) == 2); -
10998 - -
10999 - get_rule_expr((Node *) linitial(xexpr->args), -
11000 - context, true); -
11001 - -
11002 - con = lsecond_node(Const, xexpr->args); -
11003 - Assert(!con->constisnull); -
11004 - if (DatumGetBool(con->constvalue)) -
11005 - appendStringInfoString(buf, -
11006 - " PRESERVE WHITESPACE"); -
11007 - else -
11008 - appendStringInfoString(buf, -
11009 - " STRIP WHITESPACE"); -
11010 - break; -
11011 - case IS_XMLROOT: -
11012 - Assert(list_length(xexpr->args) == 3); -
11013 - -
11014 - get_rule_expr((Node *) linitial(xexpr->args), -
11015 - context, true); -
11016 - -
11017 - appendStringInfoString(buf, ", VERSION "); -
11018 - con = (Const *) lsecond(xexpr->args); -
11019 - if (IsA(con, Const) && -
11020 - con->constisnull) -
11021 - appendStringInfoString(buf, "NO VALUE"); -
11022 - else -
11023 - get_rule_expr((Node *) con, context, false); -
11024 - -
11025 - con = lthird_node(Const, xexpr->args); -
11026 - if (con->constisnull) -
11027 - /* suppress STANDALONE NO VALUE */ ; -
11028 - else -
11029 - { -
11030 - switch (DatumGetInt32(con->constvalue)) -
11031 - { -
11032 - case XML_STANDALONE_YES: -
11033 - appendStringInfoString(buf, -
11034 - ", STANDALONE YES"); -
11035 - break; -
11036 - case XML_STANDALONE_NO: -
11037 - appendStringInfoString(buf, -
11038 - ", STANDALONE NO"); -
11039 - break; -
11040 - case XML_STANDALONE_NO_VALUE: -
11041 - appendStringInfoString(buf, -
11042 - ", STANDALONE NO VALUE"); -
11043 - break; -
11044 - default: -
11045 - break; -
11046 - } -
11047 - } -
11048 - break; -
11049 - case IS_DOCUMENT: -
11050 - get_rule_expr_paren((Node *) xexpr->args, context, false, node); -
11051 - break; -
11052 - } -
11053 - } -
11054 - if (xexpr->op == IS_XMLSERIALIZE) -
11055 - { -
11056 - appendStringInfo(buf, " AS %s", -
11057 - format_type_with_typemod(xexpr->type, -
11058 - xexpr->typmod)); -
11059 - if (xexpr->indent) -
11060 - appendStringInfoString(buf, " INDENT"); -
11061 - else -
11062 - appendStringInfoString(buf, " NO INDENT"); -
11063 - } -
11064 - -
11065 - if (xexpr->op == IS_DOCUMENT) -
11066 - appendStringInfoString(buf, " IS DOCUMENT"); -
11067 - else -
11068 - appendStringInfoChar(buf, ')'); -
11069 - } -
11070 - break; -
11071 - -
11072 - case T_NullTest: -
11073 - { -
11074 - NullTest *ntest = (NullTest *) node; -
11075 - -
11076 - if (!PRETTY_PAREN(context)) -
11077 - appendStringInfoChar(buf, '('); -
11078 - get_rule_expr_paren((Node *) ntest->arg, context, true, node); -
11079 - -
11080 - /* -
11081 - * For scalar inputs, we prefer to print as IS [NOT] NULL, -
11082 - * which is shorter and traditional. If it's a rowtype input -
11083 - * but we're applying a scalar test, must print IS [NOT] -
11084 - * DISTINCT FROM NULL to be semantically correct. -
11085 - */ -
11086 - if (ntest->argisrow || -
11087 - !type_is_rowtype(exprType((Node *) ntest->arg))) -
11088 - { -
11089 - switch (ntest->nulltesttype) -
11090 - { -
11091 - case IS_NULL: -
11092 - appendStringInfoString(buf, " IS NULL"); -
11093 - break; -
11094 - case IS_NOT_NULL: -
11095 - appendStringInfoString(buf, " IS NOT NULL"); -
11096 - break; -
11097 - default: -
11098 - elog(ERROR, "unrecognized nulltesttype: %d", -
11099 - (int) ntest->nulltesttype); -
11100 - } -
11101 - } -
11102 - else -
11103 - { -
11104 - switch (ntest->nulltesttype) -
11105 - { -
11106 - case IS_NULL: -
11107 - appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL"); -
11108 - break; -
11109 - case IS_NOT_NULL: -
11110 - appendStringInfoString(buf, " IS DISTINCT FROM NULL"); -
11111 - break; -
11112 - default: -
11113 - elog(ERROR, "unrecognized nulltesttype: %d", -
11114 - (int) ntest->nulltesttype); -
11115 - } -
11116 - } -
11117 - if (!PRETTY_PAREN(context)) -
11118 - appendStringInfoChar(buf, ')'); -
11119 - } -
11120 - break; -
11121 - -
11122 - case T_BooleanTest: -
11123 - { -
11124 - BooleanTest *btest = (BooleanTest *) node; -
11125 - -
11126 - if (!PRETTY_PAREN(context)) -
11127 - appendStringInfoChar(buf, '('); -
11128 - get_rule_expr_paren((Node *) btest->arg, context, false, node); -
11129 - switch (btest->booltesttype) -
11130 - { -
11131 - case IS_TRUE: -
11132 - appendStringInfoString(buf, " IS TRUE"); -
11133 - break; -
11134 - case IS_NOT_TRUE: -
11135 - appendStringInfoString(buf, " IS NOT TRUE"); -
11136 - break; -
11137 - case IS_FALSE: -
11138 - appendStringInfoString(buf, " IS FALSE"); -
11139 - break; -
11140 - case IS_NOT_FALSE: -
11141 - appendStringInfoString(buf, " IS NOT FALSE"); -
11142 - break; -
11143 - case IS_UNKNOWN: -
11144 - appendStringInfoString(buf, " IS UNKNOWN"); -
11145 - break; -
11146 - case IS_NOT_UNKNOWN: -
11147 - appendStringInfoString(buf, " IS NOT UNKNOWN"); -
11148 - break; -
11149 - default: -
11150 - elog(ERROR, "unrecognized booltesttype: %d", -
11151 - (int) btest->booltesttype); -
11152 - } -
11153 - if (!PRETTY_PAREN(context)) -
11154 - appendStringInfoChar(buf, ')'); -
11155 - } -
11156 - break; -
11157 - -
11158 - case T_CoerceToDomain: -
11159 - { -
11160 - CoerceToDomain *ctest = (CoerceToDomain *) node; -
11161 - Node *arg = (Node *) ctest->arg; -
11162 - -
11163 - if (ctest->coercionformat == COERCE_IMPLICIT_CAST && -
11164 - !showimplicit) -
11165 - { -
11166 - /* don't show the implicit cast */ -
11167 - get_rule_expr(arg, context, false); -
11168 - } -
11169 - else -
11170 - { -
11171 - get_coercion_expr(arg, context, -
11172 - ctest->resulttype, -
11173 - ctest->resulttypmod, -
11174 - node); -
11175 - } -
11176 - } -
11177 - break; -
11178 - -
11179 - case T_CoerceToDomainValue: -
11180 - appendStringInfoString(buf, "VALUE"); -
11181 - break; -
11182 - -
11183 - case T_SetToDefault: -
11184 - appendStringInfoString(buf, "DEFAULT"); -
11185 - break; -
11186 - -
11187 - case T_CurrentOfExpr: -
11188 - { -
11189 - CurrentOfExpr *cexpr = (CurrentOfExpr *) node; -
11190 - -
11191 - if (cexpr->cursor_name) -
11192 - appendStringInfo(buf, "CURRENT OF %s", -
11193 - quote_identifier(cexpr->cursor_name)); -
11194 - else -
11195 - appendStringInfo(buf, "CURRENT OF $%d", -
11196 - cexpr->cursor_param); -
11197 - } -
11198 - break; -
11199 - -
11200 - case T_NextValueExpr: -
11201 - { -
11202 - NextValueExpr *nvexpr = (NextValueExpr *) node; -
11203 - -
11204 - /* -
11205 - * This isn't exactly nextval(), but that seems close enough -
11206 - * for EXPLAIN's purposes. -
11207 - */ -
11208 - appendStringInfoString(buf, "nextval("); -
11209 - simple_quote_literal(buf, -
11210 - generate_relation_name(nvexpr->seqid, -
11211 - NIL)); -
11212 - appendStringInfoChar(buf, ')'); -
11213 - } -
11214 - break; -
11215 - -
11216 - case T_InferenceElem: -
11217 - { -
11218 - InferenceElem *iexpr = (InferenceElem *) node; -
11219 - bool save_varprefix; -
11220 - bool need_parens; -
11221 - -
11222 - /* -
11223 - * InferenceElem can only refer to target relation, so a -
11224 - * prefix is not useful, and indeed would cause parse errors. -
11225 - */ -
11226 - save_varprefix = context->varprefix; -
11227 - context->varprefix = false; -
11228 - -
11229 - /* -
11230 - * Parenthesize the element unless it's a simple Var or a bare -
11231 - * function call. Follows pg_get_indexdef_worker(). -
11232 - */ -
11233 - need_parens = !IsA(iexpr->expr, Var); -
11234 - if (IsA(iexpr->expr, FuncExpr) && -
11235 - ((FuncExpr *) iexpr->expr)->funcformat == -
11236 - COERCE_EXPLICIT_CALL) -
11237 - need_parens = false; -
11238 - -
11239 - if (need_parens) -
11240 - appendStringInfoChar(buf, '('); -
11241 - get_rule_expr((Node *) iexpr->expr, -
11242 - context, false); -
11243 - if (need_parens) -
11244 - appendStringInfoChar(buf, ')'); -
11245 - -
11246 - context->varprefix = save_varprefix; -
11247 - -
11248 - if (iexpr->infercollid) -
11249 - appendStringInfo(buf, " COLLATE %s", -
11250 - generate_collation_name(iexpr->infercollid)); -
11251 - -
11252 - /* Add the operator class name, if not default */ -
11253 - if (iexpr->inferopclass) -
11254 - { -
11255 - Oid inferopclass = iexpr->inferopclass; -
11256 - Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass); -
11257 - -
11258 - get_opclass_name(inferopclass, inferopcinputtype, buf); -
11259 - } -
11260 - } -
11261 - break; -
11262 - -
11263 - case T_ReturningExpr: -
11264 - { -
11265 - ReturningExpr *retExpr = (ReturningExpr *) node; -
11266 - -
11267 - /* -
11268 - * We cannot see a ReturningExpr in rule deparsing, only while -
11269 - * EXPLAINing a query plan (ReturningExpr nodes are only ever -
11270 - * adding during query rewriting). Just display the expression -
11271 - * returned (an expanded view column). -
11272 - */ -
11273 - get_rule_expr((Node *) retExpr->retexpr, context, showimplicit); -
11274 - } -
11275 - break; -
11276 - -
11277 - case T_PartitionBoundSpec: -
11278 - { -
11279 - PartitionBoundSpec *spec = (PartitionBoundSpec *) node; -
11280 - ListCell *cell; -
11281 - char *sep; -
11282 - -
11283 - if (spec->is_default) -
11284 - { -
11285 - appendStringInfoString(buf, "DEFAULT"); -
11286 - break; -
11287 - } -
11288 - -
11289 - switch (spec->strategy) -
11290 - { -
11291 - case PARTITION_STRATEGY_HASH: -
11292 - Assert(spec->modulus > 0 && spec->remainder >= 0); -
11293 - Assert(spec->modulus > spec->remainder); -
11294 - -
11295 - appendStringInfoString(buf, "FOR VALUES"); -
11296 - appendStringInfo(buf, " WITH (modulus %d, remainder %d)", -
11297 - spec->modulus, spec->remainder); -
11298 - break; -
11299 - -
11300 - case PARTITION_STRATEGY_LIST: -
11301 - Assert(spec->listdatums != NIL); -
11302 - -
11303 - appendStringInfoString(buf, "FOR VALUES IN ("); -
11304 - sep = ""; -
11305 - foreach(cell, spec->listdatums) -
11306 - { -
11307 - Const *val = lfirst_node(Const, cell); -
11308 - -
11309 - appendStringInfoString(buf, sep); -
11310 - get_const_expr(val, context, -1); -
11311 - sep = ", "; -
11312 - } -
11313 - -
11314 - appendStringInfoChar(buf, ')'); -
11315 - break; -
11316 - -
11317 - case PARTITION_STRATEGY_RANGE: -
11318 - Assert(spec->lowerdatums != NIL && -
11319 - spec->upperdatums != NIL && -
11320 - list_length(spec->lowerdatums) == -
11321 - list_length(spec->upperdatums)); -
11322 - -
11323 - appendStringInfo(buf, "FOR VALUES FROM %s TO %s", -
11324 - get_range_partbound_string(spec->lowerdatums), -
11325 - get_range_partbound_string(spec->upperdatums)); -
11326 - break; -
11327 - -
11328 - default: -
11329 - elog(ERROR, "unrecognized partition strategy: %d", -
11330 - (int) spec->strategy); -
11331 - break; -
11332 - } -
11333 - } -
11334 - break; -
11335 - -
11336 - case T_JsonValueExpr: -
11337 - { -
11338 - JsonValueExpr *jve = (JsonValueExpr *) node; -
11339 - -
11340 - get_rule_expr((Node *) jve->raw_expr, context, false); -
11341 - get_json_format(jve->format, context->buf); -
11342 - } -
11343 - break; -
11344 - -
11345 - case T_JsonConstructorExpr: -
11346 - get_json_constructor((JsonConstructorExpr *) node, context, false); -
11347 - break; -
11348 - -
11349 - case T_JsonIsPredicate: -
11350 - { -
11351 - JsonIsPredicate *pred = (JsonIsPredicate *) node; -
11352 - -
11353 - if (!PRETTY_PAREN(context)) -
11354 - appendStringInfoChar(context->buf, '('); -
11355 - -
11356 - get_rule_expr_paren(pred->expr, context, true, node); -
11357 - -
11358 - appendStringInfoString(context->buf, " IS JSON"); -
11359 - -
11360 - /* TODO: handle FORMAT clause */ -
11361 - -
11362 - switch (pred->item_type) -
11363 - { -
11364 - case JS_TYPE_SCALAR: -
11365 - appendStringInfoString(context->buf, " SCALAR"); -
11366 - break; -
11367 - case JS_TYPE_ARRAY: -
11368 - appendStringInfoString(context->buf, " ARRAY"); -
11369 - break; -
11370 - case JS_TYPE_OBJECT: -
11371 - appendStringInfoString(context->buf, " OBJECT"); -
11372 - break; -
11373 - default: -
11374 - break; -
11375 - } -
11376 - -
11377 - if (pred->unique_keys) -
11378 - appendStringInfoString(context->buf, " WITH UNIQUE KEYS"); -
11379 - -
11380 - if (!PRETTY_PAREN(context)) -
11381 - appendStringInfoChar(context->buf, ')'); -
11382 - } -
11383 - break; -
11384 - -
11385 - case T_JsonExpr: -
11386 - { -
11387 - JsonExpr *jexpr = (JsonExpr *) node; -
11388 - -
11389 - switch (jexpr->op) -
11390 - { -
11391 - case JSON_EXISTS_OP: -
11392 - appendStringInfoString(buf, "JSON_EXISTS("); -
11393 - break; -
11394 - case JSON_QUERY_OP: -
11395 - appendStringInfoString(buf, "JSON_QUERY("); -
11396 - break; -
11397 - case JSON_VALUE_OP: -
11398 - appendStringInfoString(buf, "JSON_VALUE("); -
11399 - break; -
11400 - default: -
11401 - elog(ERROR, "unrecognized JsonExpr op: %d", -
11402 - (int) jexpr->op); -
11403 - } -
11404 - -
11405 - get_rule_expr(jexpr->formatted_expr, context, showimplicit); -
11406 - -
11407 - appendStringInfoString(buf, ", "); -
11408 - -
11409 - get_json_path_spec(jexpr->path_spec, context, showimplicit); -
11410 - -
11411 - if (jexpr->passing_values) -
11412 - { -
11413 - ListCell *lc1, -
11414 - *lc2; -
11415 - bool needcomma = false; -
11416 - -
11417 - appendStringInfoString(buf, " PASSING "); -
11418 - -
11419 - forboth(lc1, jexpr->passing_names, -
11420 - lc2, jexpr->passing_values) -
11421 - { -
11422 - if (needcomma) -
11423 - appendStringInfoString(buf, ", "); -
11424 - needcomma = true; -
11425 - -
11426 - get_rule_expr((Node *) lfirst(lc2), context, showimplicit); -
11427 - appendStringInfo(buf, " AS %s", -
11428 - quote_identifier(lfirst_node(String, lc1)->sval)); -
11429 - } -
11430 - } -
11431 - -
11432 - if (jexpr->op != JSON_EXISTS_OP || -
11433 - jexpr->returning->typid != BOOLOID) -
11434 - get_json_returning(jexpr->returning, context->buf, -
11435 - jexpr->op == JSON_QUERY_OP); -
11436 - -
11437 - get_json_expr_options(jexpr, context, -
11438 - jexpr->op != JSON_EXISTS_OP ? -
11439 - JSON_BEHAVIOR_NULL : -
11440 - JSON_BEHAVIOR_FALSE); -
11441 - -
11442 - appendStringInfoChar(buf, ')'); -
11443 - } -
11444 - break; -
11445 - -
11446 - case T_List: -
11447 - { -
11448 - char *sep; -
11449 - ListCell *l; -
11450 - -
11451 - sep = ""; -
11452 - foreach(l, (List *) node) -
11453 - { -
11454 - appendStringInfoString(buf, sep); -
11455 - get_rule_expr((Node *) lfirst(l), context, showimplicit); -
11456 - sep = ", "; -
11457 - } -
11458 - } -
11459 - break; -
11460 - -
11461 - case T_TableFunc: -
11462 - get_tablefunc((TableFunc *) node, context, showimplicit); -
11463 - break; -
11464 - -
11465 - case T_GraphPropertyRef: -
11466 - { -
11467 - GraphPropertyRef *gpr = (GraphPropertyRef *) node; -
11468 - -
11469 - appendStringInfo(buf, "%s.%s", quote_identifier(gpr->elvarname), quote_identifier(get_propgraph_property_name(gpr->propid))); -
11470 - break; -
11471 - } -
11472 - -
11473 - default: -
11474 - elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); -
11475 - break; -
11476 - } -
11477 - } -
get_func_expr() lines 11631-11722
Modified Lines Coverage: 2/2 lines (100.0%)
LineHitsSourceCommit
11631 - get_func_expr(FuncExpr *expr, deparse_context *context, -
11632 - bool showimplicit) -
11633 - { -
11634 - StringInfo buf = context->buf; -
11635 - Oid funcoid = expr->funcid; -
11636 - Oid argtypes[FUNC_MAX_ARGS]; -
11637 - int nargs; -
11638 - List *argnames; -
11639 - bool use_variadic; -
11640 - ListCell *l; -
11641 - -
11642 - /* -
11643 - * If the function call came from an implicit coercion, then just show the -
11644 - * first argument --- unless caller wants to see implicit coercions. -
11645 - */ -
11646 - if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit) -
11647 - { -
11648 - get_rule_expr_paren((Node *) linitial(expr->args), context, -
11649 - false, (Node *) expr); -
11650 - return; -
11651 - } -
11652 - -
11653 - /* -
11654 - * If the function call came from a cast, then show the first argument -
11655 - * plus an explicit cast operation. -
11656 - */ -
11657 - if (expr->funcformat == COERCE_EXPLICIT_CAST || -
11658 - expr->funcformat == COERCE_IMPLICIT_CAST) -
11659 - { -
11660 - Node *arg = linitial(expr->args); -
11661 - Oid rettype = expr->funcresulttype; -
11662 - int32 coercedTypmod; -
11663 - -
11664 - /* Get the typmod if this is a length-coercion function */ -
11665 - (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod); -
11666 - -
11667 - get_coercion_expr(arg, context, -
11668 - rettype, coercedTypmod, -
11669 - (Node *) expr); -
11670 - -
11671 - return; -
11672 - } -
11673 - -
11674 - /* -
11675 - * If the function was called using one of the SQL spec's random special -
11676 - * syntaxes, try to reproduce that. If we don't recognize the function, -
11677 - * fall through. -
11678 - */ -
11679 - if (expr->funcformat == COERCE_SQL_SYNTAX) -
11680 - { -
11681 - if (get_func_sql_syntax(expr, context)) -
11682 - return; -
11683 - } -
11684 - -
11685 - /* -
11686 - * Normal function: display as proname(args). First we need to extract -
11687 - * the argument datatypes. -
11688 - */ -
11689 - if (list_length(expr->args) > FUNC_MAX_ARGS) -
11690 - ereport(ERROR, -
11691 - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), -
11692 - errmsg("too many arguments"))); -
11693 - nargs = 0; -
11694 - argnames = NIL; -
11695 - foreach(l, expr->args) -
11696 - { -
11697 - Node *arg = (Node *) lfirst(l); -
11698 - -
11699 - if (IsA(arg, NamedArgExpr)) -
11700 - argnames = lappend(argnames, ((NamedArgExpr *) arg)->name); -
11701 - argtypes[nargs] = exprType(arg); -
11702 - nargs++; -
11703 - } -
11704 - -
11705 - appendStringInfo(buf, "%s(", -
11706 - generate_function_name(funcoid, nargs, -
11707 - argnames, argtypes, -
11708 - expr->funcvariadic, -
11709 - &use_variadic, -
11710 7803 context->inGroupBy, 7f135d9Recognize row pattern navigation operations by name in DEFINE
11711 7803 context->inRPRDefine)); 7f135d9Recognize row pattern navigation operations by name in DEFINE
11712 - nargs = 0; -
11713 - foreach(l, expr->args) -
11714 - { -
11715 - if (nargs++ > 0) -
11716 - appendStringInfoString(buf, ", "); -
11717 - if (use_variadic && lnext(expr->args, l) == NULL) -
11718 - appendStringInfoString(buf, "VARIADIC "); -
11719 - get_rule_expr((Node *) lfirst(l), context, true); -
11720 - } -
11721 - appendStringInfoChar(buf, ')'); -
11722 - } -
get_agg_expr_helper() lines 11740-11859
Modified Lines Coverage: 2/2 lines (100.0%)
LineHitsSourceCommit
11740 - get_agg_expr_helper(Aggref *aggref, deparse_context *context, -
11741 - Aggref *original_aggref, const char *funcname, -
11742 - const char *options, bool is_json_objectagg) -
11743 - { -
11744 - StringInfo buf = context->buf; -
11745 - Oid argtypes[FUNC_MAX_ARGS]; -
11746 - int nargs; -
11747 - bool use_variadic = false; -
11748 - -
11749 - /* -
11750 - * For a combining aggregate, we look up and deparse the corresponding -
11751 - * partial aggregate instead. This is necessary because our input -
11752 - * argument list has been replaced; the new argument list always has just -
11753 - * one element, which will point to a partial Aggref that supplies us with -
11754 - * transition states to combine. -
11755 - */ -
11756 - if (DO_AGGSPLIT_COMBINE(aggref->aggsplit)) -
11757 - { -
11758 - TargetEntry *tle; -
11759 - -
11760 - Assert(list_length(aggref->args) == 1); -
11761 - tle = linitial_node(TargetEntry, aggref->args); -
11762 - resolve_special_varno((Node *) tle->expr, context, -
11763 - get_agg_combine_expr, original_aggref); -
11764 - return; -
11765 - } -
11766 - -
11767 - /* -
11768 - * Mark as PARTIAL, if appropriate. We look to the original aggref so as -
11769 - * to avoid printing this when recursing from the code just above. -
11770 - */ -
11771 - if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit)) -
11772 - appendStringInfoString(buf, "PARTIAL "); -
11773 - -
11774 - /* Extract the argument types as seen by the parser */ -
11775 - nargs = get_aggregate_argtypes(aggref, argtypes); -
11776 - -
11777 - if (!funcname) -
11778 - funcname = generate_function_name(aggref->aggfnoid, nargs, NIL, -
11779 - argtypes, aggref->aggvariadic, -
11780 - &use_variadic, -
11781 2600 context->inGroupBy, 7f135d9Recognize row pattern navigation operations by name in DEFINE
11782 2600 context->inRPRDefine); 7f135d9Recognize row pattern navigation operations by name in DEFINE
11783 - -
11784 - /* Print the aggregate name, schema-qualified if needed */ -
11785 - appendStringInfo(buf, "%s(%s", funcname, -
11786 - (aggref->aggdistinct != NIL) ? "DISTINCT " : ""); -
11787 - -
11788 - if (AGGKIND_IS_ORDERED_SET(aggref->aggkind)) -
11789 - { -
11790 - /* -
11791 - * Ordered-set aggregates do not use "*" syntax. Also, we needn't -
11792 - * worry about inserting VARIADIC. So we can just dump the direct -
11793 - * args as-is. -
11794 - */ -
11795 - Assert(!aggref->aggvariadic); -
11796 - get_rule_expr((Node *) aggref->aggdirectargs, context, true); -
11797 - Assert(aggref->aggorder != NIL); -
11798 - appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY "); -
11799 - get_rule_orderby(aggref->aggorder, aggref->args, false, context); -
11800 - } -
11801 - else -
11802 - { -
11803 - /* aggstar can be set only in zero-argument aggregates */ -
11804 - if (aggref->aggstar) -
11805 - appendStringInfoChar(buf, '*'); -
11806 - else -
11807 - { -
11808 - ListCell *l; -
11809 - int i; -
11810 - -
11811 - i = 0; -
11812 - foreach(l, aggref->args) -
11813 - { -
11814 - TargetEntry *tle = (TargetEntry *) lfirst(l); -
11815 - Node *arg = (Node *) tle->expr; -
11816 - -
11817 - Assert(!IsA(arg, NamedArgExpr)); -
11818 - if (tle->resjunk) -
11819 - continue; -
11820 - if (i++ > 0) -
11821 - { -
11822 - if (is_json_objectagg) -
11823 - { -
11824 - /* -
11825 - * the ABSENT ON NULL and WITH UNIQUE args are printed -
11826 - * separately, so ignore them here -
11827 - */ -
11828 - if (i > 2) -
11829 - break; -
11830 - -
11831 - appendStringInfoString(buf, " : "); -
11832 - } -
11833 - else -
11834 - appendStringInfoString(buf, ", "); -
11835 - } -
11836 - if (use_variadic && i == nargs) -
11837 - appendStringInfoString(buf, "VARIADIC "); -
11838 - get_rule_expr(arg, context, true); -
11839 - } -
11840 - } -
11841 - -
11842 - if (aggref->aggorder != NIL) -
11843 - { -
11844 - appendStringInfoString(buf, " ORDER BY "); -
11845 - get_rule_orderby(aggref->aggorder, aggref->args, false, context); -
11846 - } -
11847 - } -
11848 - -
11849 - if (options) -
11850 - appendStringInfoString(buf, options); -
11851 - -
11852 - if (aggref->aggfilter != NULL) -
11853 - { -
11854 - appendStringInfoString(buf, ") FILTER (WHERE "); -
11855 - get_rule_expr((Node *) aggref->aggfilter, context, false); -
11856 - } -
11857 - -
11858 - appendStringInfoChar(buf, ')'); -
11859 - } -
get_windowfunc_expr_helper() lines 11894-12004
Modified Lines Coverage: 2/2 lines (100.0%)
LineHitsSourceCommit
11894 - get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context, -
11895 - const char *funcname, const char *options, -
11896 - bool is_json_objectagg) -
11897 - { -
11898 - StringInfo buf = context->buf; -
11899 - Oid argtypes[FUNC_MAX_ARGS]; -
11900 - int nargs; -
11901 - List *argnames; -
11902 - ListCell *l; -
11903 - -
11904 - if (list_length(wfunc->args) > FUNC_MAX_ARGS) -
11905 - ereport(ERROR, -
11906 - (errcode(ERRCODE_TOO_MANY_ARGUMENTS), -
11907 - errmsg("too many arguments"))); -
11908 - nargs = 0; -
11909 - argnames = NIL; -
11910 - foreach(l, wfunc->args) -
11911 - { -
11912 - Node *arg = (Node *) lfirst(l); -
11913 - -
11914 - if (IsA(arg, NamedArgExpr)) -
11915 - argnames = lappend(argnames, ((NamedArgExpr *) arg)->name); -
11916 - argtypes[nargs] = exprType(arg); -
11917 - nargs++; -
11918 - } -
11919 - -
11920 - if (!funcname) -
11921 - funcname = generate_function_name(wfunc->winfnoid, nargs, argnames, -
11922 - argtypes, false, NULL, -
11923 2495 context->inGroupBy, 7f135d9Recognize row pattern navigation operations by name in DEFINE
11924 2495 context->inRPRDefine); 7f135d9Recognize row pattern navigation operations by name in DEFINE
11925 - -
11926 - appendStringInfo(buf, "%s(", funcname); -
11927 - -
11928 - /* winstar can be set only in zero-argument aggregates */ -
11929 - if (wfunc->winstar) -
11930 - appendStringInfoChar(buf, '*'); -
11931 - else -
11932 - { -
11933 - if (is_json_objectagg) -
11934 - { -
11935 - get_rule_expr((Node *) linitial(wfunc->args), context, false); -
11936 - appendStringInfoString(buf, " : "); -
11937 - get_rule_expr((Node *) lsecond(wfunc->args), context, false); -
11938 - } -
11939 - else -
11940 - get_rule_expr((Node *) wfunc->args, context, true); -
11941 - } -
11942 - -
11943 - if (options) -
11944 - appendStringInfoString(buf, options); -
11945 - -
11946 - if (wfunc->aggfilter != NULL) -
11947 - { -
11948 - appendStringInfoString(buf, ") FILTER (WHERE "); -
11949 - get_rule_expr((Node *) wfunc->aggfilter, context, false); -
11950 - } -
11951 - -
11952 - appendStringInfoString(buf, ") "); -
11953 - -
11954 - if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS) -
11955 - appendStringInfoString(buf, "IGNORE NULLS "); -
11956 - -
11957 - appendStringInfoString(buf, "OVER "); -
11958 - -
11959 - if (context->windowClause) -
11960 - { -
11961 - /* Query-decompilation case: search the windowClause list */ -
11962 - foreach(l, context->windowClause) -
11963 - { -
11964 - WindowClause *wc = (WindowClause *) lfirst(l); -
11965 - -
11966 - if (wc->winref == wfunc->winref) -
11967 - { -
11968 - if (wc->name) -
11969 - appendStringInfoString(buf, quote_identifier(wc->name)); -
11970 - else -
11971 - get_rule_windowspec(wc, context->targetList, context); -
11972 - break; -
11973 - } -
11974 - } -
11975 - if (l == NULL) -
11976 - elog(ERROR, "could not find window clause for winref %u", -
11977 - wfunc->winref); -
11978 - } -
11979 - else -
11980 - { -
11981 - /* -
11982 - * In EXPLAIN, search the namespace stack for a matching WindowAgg -
11983 - * node (probably it's always the first entry), and print winname. -
11984 - */ -
11985 - foreach(l, context->namespaces) -
11986 - { -
11987 - deparse_namespace *dpns = (deparse_namespace *) lfirst(l); -
11988 - -
11989 - if (dpns->plan && IsA(dpns->plan, WindowAgg)) -
11990 - { -
11991 - WindowAgg *wagg = (WindowAgg *) dpns->plan; -
11992 - -
11993 - if (wagg->winref == wfunc->winref) -
11994 - { -
11995 - appendStringInfoString(buf, quote_identifier(wagg->winname)); -
11996 - break; -
11997 - } -
11998 - } -
11999 - } -
12000 - if (l == NULL) -
12001 - elog(ERROR, "could not find window clause for winref %u", -
12002 - wfunc->winref); -
12003 - } -
12004 - } -
get_tablesample_def() lines 13750-13782
Modified Lines Coverage: 0/0 lines (0.0%)
LineHitsSourceCommit
13750 - get_tablesample_def(TableSampleClause *tablesample, deparse_context *context) -
13751 - { -
13752 - StringInfo buf = context->buf; -
13753 - Oid argtypes[1]; -
13754 - int nargs; -
13755 - ListCell *l; -
13756 - -
13757 - /* -
13758 - * We should qualify the handler's function name if it wouldn't be -
13759 - * resolved by lookup in the current search path. -
13760 - */ -
13761 - argtypes[0] = INTERNALOID; -
13762 - appendStringInfo(buf, " TABLESAMPLE %s (", -
13763 - generate_function_name(tablesample->tsmhandler, 1, -
13764 - NIL, argtypes, -
13765 - false, NULL, false, false)); 7f135d9Recognize row pattern navigation operations by name in DEFINE
13766 - -
13767 - nargs = 0; -
13768 - foreach(l, tablesample->args) -
13769 - { -
13770 - if (nargs++ > 0) -
13771 - appendStringInfoString(buf, ", "); -
13772 - get_rule_expr((Node *) lfirst(l), context, false); -
13773 - } -
13774 - appendStringInfoChar(buf, ')'); -
13775 - -
13776 - if (tablesample->repeatable != NULL) -
13777 - { -
13778 - appendStringInfoString(buf, " REPEATABLE ("); -
13779 - get_rule_expr((Node *) tablesample->repeatable, context, false); -
13780 - appendStringInfoChar(buf, ')'); -
13781 - } -
13782 - } -
generate_function_name() lines 14184-14295
Modified Lines Coverage: 3/6 lines (50.0%)
LineHitsSourceCommit
14184 - generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes, -
14185 - bool has_variadic, bool *use_variadic_p, -
14186 - bool inGroupBy, bool inRPRDefine) 7f135d9Recognize row pattern navigation operations by name in DEFINE
14187 - { -
14188 - char *result; -
14189 - HeapTuple proctup; -
14190 - Form_pg_proc procform; -
14191 - char *proname; -
14192 - bool use_variadic; -
14193 - char *nspname; -
14194 - FuncDetailCode p_result; -
14195 - int fgc_flags; -
14196 - Oid p_funcid; -
14197 - Oid p_rettype; -
14198 - bool p_retset; -
14199 - int p_nvargs; -
14200 - Oid p_vatype; -
14201 - Oid *p_true_typeids; -
14202 - bool force_qualify = false; -
14203 - -
14204 - proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); -
14205 - if (!HeapTupleIsValid(proctup)) -
14206 - elog(ERROR, "cache lookup failed for function %u", funcid); -
14207 - procform = (Form_pg_proc) GETSTRUCT(proctup); -
14208 - proname = NameStr(procform->proname); -
14209 - -
14210 - /* -
14211 - * Due to parser hacks to avoid needing to reserve CUBE, we need to force -
14212 - * qualification of some function names within GROUP BY. -
14213 - */ -
14214 - if (inGroupBy) -
14215 - { -
14216 - if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0) -
14217 - force_qualify = true; -
14218 - } -
14219 - -
14220 - /* 7f135d9Recognize row pattern navigation operations by name in DEFINE
14221 - * Inside a row pattern DEFINE clause, the parser binds an unqualified 7f135d9Recognize row pattern navigation operations by name in DEFINE
14222 - * prev/next/first/last to a navigation operation before any catalog 7f135d9Recognize row pattern navigation operations by name in DEFINE
14223 - * lookup, so an unqualified call to a user function of one of those names 7f135d9Recognize row pattern navigation operations by name in DEFINE
14224 - * would change meaning across a deparse/reparse cycle. Force schema 7f135d9Recognize row pattern navigation operations by name in DEFINE
14225 - * qualification; the qualified form is the documented escape hatch. Only 7f135d9Recognize row pattern navigation operations by name in DEFINE
14226 - * the exact lower-case names are at risk: a mixed-case proname deparses 7f135d9Recognize row pattern navigation operations by name in DEFINE
14227 - * quoted and cannot match the parser's downcased comparison. 7f135d9Recognize row pattern navigation operations by name in DEFINE
14228 - */ 7f135d9Recognize row pattern navigation operations by name in DEFINE
14229 13673 if (inRPRDefine) 7f135d9Recognize row pattern navigation operations by name in DEFINE
14230 - { 7f135d9Recognize row pattern navigation operations by name in DEFINE
14231 4 if (strcmp(proname, "prev") == 0 || 7f135d9Recognize row pattern navigation operations by name in DEFINE
14232 0 strcmp(proname, "next") == 0 || 7f135d9Recognize row pattern navigation operations by name in DEFINE
ReachableTestable · confidence high · generate_function_name @14232-14234 · 3 lines
How to test
Use the proposed test verbatim;
it is sufficient and was empirically validated.
Add to rpr_base.sql in the rpr_navns block (after the existing navns_fn view, before DROP SCHEMA rpr_navns CASCADE at line ~1532).
A single `last` view covers all three lines via || short-circuit:

CREATE FUNCTION rpr_navns.last(integer) RETURNS integer AS 'SELECT -999' LANGUAGE sql IMMUTABLE;
CREATE VIEW navns_fn_last AS
SELECT id, count(*) OVER w AS cnt
FROM nt
WINDOW w AS (PARTITION BY g
ORDER BY id
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING INITIAL
PATTERN (A+)
DEFINE A AS rpr_navns.last(val) = -999);
SELECT pg_get_viewdef('navns_fn_last');
DROP VIEW navns_fn_last;
DROP FUNCTION rpr_navns.last(integer);

Confirmed expected viewdef output preserves the qualified call:
DEFINE
  a AS (rpr_navns.last(val) = '-999'::integer) )
Capture this exact deparse in rpr_base.out.
Note the deparse also emits `AFTER MATCH SKIP PAST LAST ROW` (default) on its own line -- include it in expected output.
If a reviewer prefers explicit coverage intent, optionally also add next/first views, but they are redundant for line coverage.
Verdict
CONFIRMED testable and correctly diagnosed.
Lines 14231-14234 are the prev/next/first/last arms of a short-circuited || chain inside the `if (inRPRDefine)` force-qualify block (ruleutils.c:14229-14235).
I verified the code, the deparse mechanism, and ran the proposed test against the actual RPR build (tmp_install) in a live temp cluster.
Results:
(1) inRPRDefine is set true while walking
DEFINE TargetEntries (get_rule_pattern_define, ruleutils.c:7237) so any FuncExpr there hits generate_function_name with inRPRDefine=true;
(2) deparsing a user function `rpr_navns.last(val)` inside
DEFINE yields viewdef text that preserves the schema-qualified call (`rpr_navns.last(val) = '-999'::integer`) and re-parses cleanly, proving force_qualify=true fired;
(3) because `||` is left-to-right short-circuit, proname='last' evaluates strcmp(prev)==false (14231), strcmp(next)==false (14232), strcmp(first)==false (14233), strcmp(last)==true (14234) -- so the single `last` test drives execution through all three uncovered lines.
I also independently confirmed `next` and `first` named user functions force qualification and round-trip.
The existing navns_fn view (rpr_base.sql:1525-1529) only covers the `prev` arm (14231), matching the gcov gap exactly.
No refutation found;
the lines are not defensive/dead.
14233 0 strcmp(proname, "first") == 0 || 7f135d9Recognize row pattern navigation operations by name in DEFINE
14234 0 strcmp(proname, "last") == 0) 7f135d9Recognize row pattern navigation operations by name in DEFINE
14235 4 force_qualify = true; 7f135d9Recognize row pattern navigation operations by name in DEFINE
14236 - } 7f135d9Recognize row pattern navigation operations by name in DEFINE
14237 - 7f135d9Recognize row pattern navigation operations by name in DEFINE
14238 - /* -
14239 - * Determine whether VARIADIC should be printed. We must do this first -
14240 - * since it affects the lookup rules in func_get_detail(). -
14241 - * -
14242 - * We always print VARIADIC if the function has a merged variadic-array -
14243 - * argument. Note that this is always the case for functions taking a -
14244 - * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC -
14245 - * and printed the array elements as separate arguments, the call could -
14246 - * match a newer non-VARIADIC function. -
14247 - */ -
14248 - if (use_variadic_p) -
14249 - { -
14250 - /* Parser should not have set funcvariadic unless fn is variadic */ -
14251 - Assert(!has_variadic || OidIsValid(procform->provariadic)); -
14252 - use_variadic = has_variadic; -
14253 - *use_variadic_p = use_variadic; -
14254 - } -
14255 - else -
14256 - { -
14257 - Assert(!has_variadic); -
14258 - use_variadic = false; -
14259 - } -
14260 - -
14261 - /* -
14262 - * The idea here is to schema-qualify only if the parser would fail to -
14263 - * resolve the correct function given the unqualified func name with the -
14264 - * specified argtypes and VARIADIC flag. But if we already decided to -
14265 - * force qualification, then we can skip the lookup and pretend we didn't -
14266 - * find it. -
14267 - */ -
14268 - if (!force_qualify) -
14269 - p_result = func_get_detail(list_make1(makeString(proname)), -
14270 - NIL, argnames, nargs, argtypes, -
14271 - !use_variadic, true, false, -
14272 - &fgc_flags, -
14273 - &p_funcid, &p_rettype, -
14274 - &p_retset, &p_nvargs, &p_vatype, -
14275 - &p_true_typeids, NULL); -
14276 - else -
14277 - { -
14278 - p_result = FUNCDETAIL_NOTFOUND; -
14279 - p_funcid = InvalidOid; -
14280 - } -
14281 - -
14282 - if ((p_result == FUNCDETAIL_NORMAL || -
14283 - p_result == FUNCDETAIL_AGGREGATE || -
14284 - p_result == FUNCDETAIL_WINDOWFUNC) && -
14285 - p_funcid == funcid) -
14286 - nspname = NULL; -
14287 - else -
14288 - nspname = get_namespace_name_or_temp(procform->pronamespace); -
14289 - -
14290 - result = quote_qualified_identifier(nspname, proname); -
14291 - -
14292 - ReleaseSysCache(proctup); -
14293 - -
14294 - return result; -
14295 - } -