src/backend/utils/adt/ruleutils.c - Coverage Report

← 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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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).
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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%)

Line | Hits | Source | Commit
--- | --- | --- | ---
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
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 | - | } | -