| 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 | - | } | - |
| 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 | - | } | - |
| 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 | - | } | - |
| 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 | - | } | - |
| 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 | - | } | - |
| 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). |
| 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). |
| 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). |
| 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). |
| 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 | - | } | - |
| 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 | - | } | - |
| 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 | - | } | - |
| 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). |
UnreachableReason Confirmed defensive-unreachable. Lines 10163-10164 are the default: arm of the switch over RPRNavExpr->kind inside the T_RPRNavExpr case in get_rule_expr (ruleutils.c). The RPRNavKind enum (primnodes.h:669-680) has exactly 8 members (RPR_NAV_PREV, NEXT, FIRST, LAST, PREV_FIRST, PREV_LAST, NEXT_FIRST, NEXT_LAST) and all 8 have explicit case labels at lines 10135-10162, so the default arm cannot match any in-range value. The kind field is assigned during parse analysis from this fixed enum set; no SQL syntax can produce an out-of-range value. The WRITE_READ_PARSE_PLAN_TREES round-trip reads back the exact integer written, so it preserves the in-range value and cannot synthesize a bad kind either. I tried to refute reachability via parser paths, optimizer rewrites, and copy/out/read node round-trips and found no input that drives an out-of-range kind into this deparse switch. Line 10166 (inner_func = NULL "keep compiler quiet") is a dead store grouped with the same defensive arm. This matches standard PostgreSQL switch-over-enum guard style used throughout ruleutils.c.Recommended fix Leave the elog(ERROR) default in place as the standard PostgreSQL defensive guard for switch-over-enum; it is consistent with neighboring node-deparse code in ruleutils.c and protects against future RPRNavKind additions. No source change recommended. If maximizing measured coverage were a hard requirement, the only options would be a coverage-exclusion marker or dropping the default to rely on -Wswitch exhaustiveness, neither of which is advisable. | |||
| 10164 | 0 | elog(ERROR, "unrecognized RPR navigation kind: %d", | 1f4b53eRow pattern recognition patch (rewriter). |
| 10165 | - | nav->kind); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10166 | - | inner_func = NULL; /* keep compiler quiet */ | 1f4b53eRow pattern recognition patch (rewriter). |
| 10167 | - | break; | 1f4b53eRow pattern recognition patch (rewriter). |
| 10168 | - | } | 1f4b53eRow pattern recognition patch (rewriter). |
| 10169 | - | 1f4b53eRow pattern recognition patch (rewriter). | |
| 10170 | 133 | if (outer_func != NULL) | 1f4b53eRow pattern recognition patch (rewriter). |
| 10171 | - | { | 1f4b53eRow pattern recognition patch (rewriter). |
| 10172 | - | /* | 1f4b53eRow pattern recognition patch (rewriter). |
| 10173 | - | * Compound: PREV(FIRST(arg [, inner_offset]) [, | 1f4b53eRow pattern recognition patch (rewriter). |
| 10174 | - | * outer_offset]) | 1f4b53eRow pattern recognition patch (rewriter). |
| 10175 | - | */ | 1f4b53eRow pattern recognition patch (rewriter). |
| 10176 | 50 | appendStringInfoString(buf, outer_func); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10177 | 50 | appendStringInfoString(buf, inner_func); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10178 | 50 | get_rule_expr((Node *) nav->arg, context, showimplicit); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10179 | 50 | if (nav->offset_arg != NULL) | 1f4b53eRow pattern recognition patch (rewriter). |
| 10180 | - | { | 1f4b53eRow pattern recognition patch (rewriter). |
| 10181 | 18 | appendStringInfoString(buf, ", "); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10182 | 18 | get_rule_expr((Node *) nav->offset_arg, context, | 1f4b53eRow pattern recognition patch (rewriter). |
| 10183 | - | showimplicit); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10184 | - | } | 1f4b53eRow pattern recognition patch (rewriter). |
| 10185 | 50 | appendStringInfoChar(buf, ')'); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10186 | 50 | if (nav->compound_offset_arg != NULL) | 1f4b53eRow pattern recognition patch (rewriter). |
| 10187 | - | { | 1f4b53eRow pattern recognition patch (rewriter). |
| 10188 | 41 | appendStringInfoString(buf, ", "); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10189 | 41 | get_rule_expr((Node *) nav->compound_offset_arg, | 1f4b53eRow pattern recognition patch (rewriter). |
| 10190 | - | context, showimplicit); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10191 | - | } | 1f4b53eRow pattern recognition patch (rewriter). |
| 10192 | 50 | appendStringInfoChar(buf, ')'); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10193 | - | } | 1f4b53eRow pattern recognition patch (rewriter). |
| 10194 | - | else | 1f4b53eRow pattern recognition patch (rewriter). |
| 10195 | - | { | 1f4b53eRow pattern recognition patch (rewriter). |
| 10196 | - | /* Simple: FUNC(arg [, offset]) */ | 1f4b53eRow pattern recognition patch (rewriter). |
| 10197 | 230 | appendStringInfoString(buf, inner_func); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10198 | 230 | get_rule_expr((Node *) nav->arg, context, showimplicit); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10199 | 230 | if (nav->offset_arg != NULL) | 1f4b53eRow pattern recognition patch (rewriter). |
| 10200 | - | { | 1f4b53eRow pattern recognition patch (rewriter). |
| 10201 | 46 | appendStringInfoString(buf, ", "); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10202 | 46 | get_rule_expr((Node *) nav->offset_arg, context, | 1f4b53eRow pattern recognition patch (rewriter). |
| 10203 | - | showimplicit); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10204 | - | } | 1f4b53eRow pattern recognition patch (rewriter). |
| 10205 | 230 | appendStringInfoChar(buf, ')'); | 1f4b53eRow pattern recognition patch (rewriter). |
| 10206 | - | } | 1f4b53eRow pattern recognition patch (rewriter). |
| 10207 | - | } | 1f4b53eRow pattern recognition patch (rewriter). |
| 10208 | - | break; | 1f4b53eRow pattern recognition patch (rewriter). |
| 10209 | - | 1f4b53eRow pattern recognition patch (rewriter). | |
| 10210 | - | case T_NamedArgExpr: | - |
| 10211 | - | { | - |
| 10212 | - | NamedArgExpr *na = (NamedArgExpr *) node; | - |
| 10213 | - | - | |
| 10214 | - | appendStringInfo(buf, "%s => ", quote_identifier(na->name)); | - |
| 10215 | - | get_rule_expr((Node *) na->arg, context, showimplicit); | - |
| 10216 | - | } | - |
| 10217 | - | break; | - |
| 10218 | - | - | |
| 10219 | - | case T_OpExpr: | - |
| 10220 | - | get_oper_expr((OpExpr *) node, context); | - |
| 10221 | - | break; | - |
| 10222 | - | - | |
| 10223 | - | case T_DistinctExpr: | - |
| 10224 | - | { | - |
| 10225 | - | DistinctExpr *expr = (DistinctExpr *) node; | - |
| 10226 | - | List *args = expr->args; | - |
| 10227 | - | Node *arg1 = (Node *) linitial(args); | - |
| 10228 | - | Node *arg2 = (Node *) lsecond(args); | - |
| 10229 | - | - | |
| 10230 | - | if (!PRETTY_PAREN(context)) | - |
| 10231 | - | appendStringInfoChar(buf, '('); | - |
| 10232 | - | get_rule_expr_paren(arg1, context, true, node); | - |
| 10233 | - | appendStringInfoString(buf, " IS DISTINCT FROM "); | - |
| 10234 | - | get_rule_expr_paren(arg2, context, true, node); | - |
| 10235 | - | if (!PRETTY_PAREN(context)) | - |
| 10236 | - | appendStringInfoChar(buf, ')'); | - |
| 10237 | - | } | - |
| 10238 | - | break; | - |
| 10239 | - | - | |
| 10240 | - | case T_NullIfExpr: | - |
| 10241 | - | { | - |
| 10242 | - | NullIfExpr *nullifexpr = (NullIfExpr *) node; | - |
| 10243 | - | - | |
| 10244 | - | appendStringInfoString(buf, "NULLIF("); | - |
| 10245 | - | get_rule_expr((Node *) nullifexpr->args, context, true); | - |
| 10246 | - | appendStringInfoChar(buf, ')'); | - |
| 10247 | - | } | - |
| 10248 | - | break; | - |
| 10249 | - | - | |
| 10250 | - | case T_ScalarArrayOpExpr: | - |
| 10251 | - | { | - |
| 10252 | - | ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; | - |
| 10253 | - | List *args = expr->args; | - |
| 10254 | - | Node *arg1 = (Node *) linitial(args); | - |
| 10255 | - | Node *arg2 = (Node *) lsecond(args); | - |
| 10256 | - | - | |
| 10257 | - | if (!PRETTY_PAREN(context)) | - |
| 10258 | - | appendStringInfoChar(buf, '('); | - |
| 10259 | - | get_rule_expr_paren(arg1, context, true, node); | - |
| 10260 | - | appendStringInfo(buf, " %s %s (", | - |
| 10261 | - | generate_operator_name(expr->opno, | - |
| 10262 | - | exprType(arg1), | - |
| 10263 | - | get_base_element_type(exprType(arg2))), | - |
| 10264 | - | expr->useOr ? "ANY" : "ALL"); | - |
| 10265 | - | get_rule_expr_paren(arg2, context, true, node); | - |
| 10266 | - | - | |
| 10267 | - | /* | - |
| 10268 | - | * There's inherent ambiguity in "x op ANY/ALL (y)" when y is | - |
| 10269 | - | * a bare sub-SELECT. Since we're here, the sub-SELECT must | - |
| 10270 | - | * be meant as a scalar sub-SELECT yielding an array value to | - |
| 10271 | - | * be used in ScalarArrayOpExpr; but the grammar will | - |
| 10272 | - | * preferentially interpret such a construct as an ANY/ALL | - |
| 10273 | - | * SubLink. To prevent misparsing the output that way, insert | - |
| 10274 | - | * a dummy coercion (which will be stripped by parse analysis, | - |
| 10275 | - | * so no inefficiency is added in dump and reload). This is | - |
| 10276 | - | * indeed most likely what the user wrote to get the construct | - |
| 10277 | - | * accepted in the first place. | - |
| 10278 | - | */ | - |
| 10279 | - | if (IsA(arg2, SubLink) && | - |
| 10280 | - | ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK) | - |
| 10281 | - | appendStringInfo(buf, "::%s", | - |
| 10282 | - | format_type_with_typemod(exprType(arg2), | - |
| 10283 | - | exprTypmod(arg2))); | - |
| 10284 | - | appendStringInfoChar(buf, ')'); | - |
| 10285 | - | if (!PRETTY_PAREN(context)) | - |
| 10286 | - | appendStringInfoChar(buf, ')'); | - |
| 10287 | - | } | - |
| 10288 | - | break; | - |
| 10289 | - | - | |
| 10290 | - | case T_BoolExpr: | - |
| 10291 | - | { | - |
| 10292 | - | BoolExpr *expr = (BoolExpr *) node; | - |
| 10293 | - | Node *first_arg = linitial(expr->args); | - |
| 10294 | - | ListCell *arg; | - |
| 10295 | - | - | |
| 10296 | - | switch (expr->boolop) | - |
| 10297 | - | { | - |
| 10298 | - | case AND_EXPR: | - |
| 10299 | - | if (!PRETTY_PAREN(context)) | - |
| 10300 | - | appendStringInfoChar(buf, '('); | - |
| 10301 | - | get_rule_expr_paren(first_arg, context, | - |
| 10302 | - | false, node); | - |
| 10303 | - | for_each_from(arg, expr->args, 1) | - |
| 10304 | - | { | - |
| 10305 | - | appendStringInfoString(buf, " AND "); | - |
| 10306 | - | get_rule_expr_paren((Node *) lfirst(arg), context, | - |
| 10307 | - | false, node); | - |
| 10308 | - | } | - |
| 10309 | - | if (!PRETTY_PAREN(context)) | - |
| 10310 | - | appendStringInfoChar(buf, ')'); | - |
| 10311 | - | break; | - |
| 10312 | - | - | |
| 10313 | - | case OR_EXPR: | - |
| 10314 | - | if (!PRETTY_PAREN(context)) | - |
| 10315 | - | appendStringInfoChar(buf, '('); | - |
| 10316 | - | get_rule_expr_paren(first_arg, context, | - |
| 10317 | - | false, node); | - |
| 10318 | - | for_each_from(arg, expr->args, 1) | - |
| 10319 | - | { | - |
| 10320 | - | appendStringInfoString(buf, " OR "); | - |
| 10321 | - | get_rule_expr_paren((Node *) lfirst(arg), context, | - |
| 10322 | - | false, node); | - |
| 10323 | - | } | - |
| 10324 | - | if (!PRETTY_PAREN(context)) | - |
| 10325 | - | appendStringInfoChar(buf, ')'); | - |
| 10326 | - | break; | - |
| 10327 | - | - | |
| 10328 | - | case NOT_EXPR: | - |
| 10329 | - | if (!PRETTY_PAREN(context)) | - |
| 10330 | - | appendStringInfoChar(buf, '('); | - |
| 10331 | - | appendStringInfoString(buf, "NOT "); | - |
| 10332 | - | get_rule_expr_paren(first_arg, context, | - |
| 10333 | - | false, node); | - |
| 10334 | - | if (!PRETTY_PAREN(context)) | - |
| 10335 | - | appendStringInfoChar(buf, ')'); | - |
| 10336 | - | break; | - |
| 10337 | - | - | |
| 10338 | - | default: | - |
| 10339 | - | elog(ERROR, "unrecognized boolop: %d", | - |
| 10340 | - | (int) expr->boolop); | - |
| 10341 | - | } | - |
| 10342 | - | } | - |
| 10343 | - | break; | - |
| 10344 | - | - | |
| 10345 | - | case T_SubLink: | - |
| 10346 | - | get_sublink_expr((SubLink *) node, context); | - |
| 10347 | - | break; | - |
| 10348 | - | - | |
| 10349 | - | case T_SubPlan: | - |
| 10350 | - | { | - |
| 10351 | - | SubPlan *subplan = (SubPlan *) node; | - |
| 10352 | - | - | |
| 10353 | - | /* | - |
| 10354 | - | * We cannot see an already-planned subplan in rule deparsing, | - |
| 10355 | - | * only while EXPLAINing a query plan. We don't try to | - |
| 10356 | - | * reconstruct the original SQL, just reference the subplan | - |
| 10357 | - | * that appears elsewhere in EXPLAIN's result. It does seem | - |
| 10358 | - | * useful to show the subLinkType and testexpr (if any), and | - |
| 10359 | - | * we also note whether the subplan will be hashed. | - |
| 10360 | - | */ | - |
| 10361 | - | switch (subplan->subLinkType) | - |
| 10362 | - | { | - |
| 10363 | - | case EXISTS_SUBLINK: | - |
| 10364 | - | appendStringInfoString(buf, "EXISTS("); | - |
| 10365 | - | Assert(subplan->testexpr == NULL); | - |
| 10366 | - | break; | - |
| 10367 | - | case ALL_SUBLINK: | - |
| 10368 | - | appendStringInfoString(buf, "(ALL "); | - |
| 10369 | - | Assert(subplan->testexpr != NULL); | - |
| 10370 | - | break; | - |
| 10371 | - | case ANY_SUBLINK: | - |
| 10372 | - | appendStringInfoString(buf, "(ANY "); | - |
| 10373 | - | Assert(subplan->testexpr != NULL); | - |
| 10374 | - | break; | - |
| 10375 | - | case ROWCOMPARE_SUBLINK: | - |
| 10376 | - | /* Parenthesizing the testexpr seems sufficient */ | - |
| 10377 | - | appendStringInfoChar(buf, '('); | - |
| 10378 | - | Assert(subplan->testexpr != NULL); | - |
| 10379 | - | break; | - |
| 10380 | - | case EXPR_SUBLINK: | - |
| 10381 | - | /* No need to decorate these subplan references */ | - |
| 10382 | - | appendStringInfoChar(buf, '('); | - |
| 10383 | - | Assert(subplan->testexpr == NULL); | - |
| 10384 | - | break; | - |
| 10385 | - | case MULTIEXPR_SUBLINK: | - |
| 10386 | - | /* MULTIEXPR isn't executed in the normal way */ | - |
| 10387 | - | appendStringInfoString(buf, "(rescan "); | - |
| 10388 | - | Assert(subplan->testexpr == NULL); | - |
| 10389 | - | break; | - |
| 10390 | - | case ARRAY_SUBLINK: | - |
| 10391 | - | appendStringInfoString(buf, "ARRAY("); | - |
| 10392 | - | Assert(subplan->testexpr == NULL); | - |
| 10393 | - | break; | - |
| 10394 | - | case CTE_SUBLINK: | - |
| 10395 | - | /* This case is unreachable within expressions */ | - |
| 10396 | - | appendStringInfoString(buf, "CTE("); | - |
| 10397 | - | Assert(subplan->testexpr == NULL); | - |
| 10398 | - | break; | - |
| 10399 | - | } | - |
| 10400 | - | - | |
| 10401 | - | if (subplan->testexpr != NULL) | - |
| 10402 | - | { | - |
| 10403 | - | deparse_namespace *dpns; | - |
| 10404 | - | - | |
| 10405 | - | /* | - |
| 10406 | - | * Push SubPlan into ancestors list while deparsing | - |
| 10407 | - | * testexpr, so that we can handle PARAM_EXEC references | - |
| 10408 | - | * to the SubPlan's paramIds. (This makes it look like | - |
| 10409 | - | * the SubPlan is an "ancestor" of the current plan node, | - |
| 10410 | - | * which is a little weird, but it does no harm.) In this | - |
| 10411 | - | * path, we don't need to mention the SubPlan explicitly, | - |
| 10412 | - | * because the referencing Params will show its existence. | - |
| 10413 | - | */ | - |
| 10414 | - | dpns = (deparse_namespace *) linitial(context->namespaces); | - |
| 10415 | - | dpns->ancestors = lcons(subplan, dpns->ancestors); | - |
| 10416 | - | - | |
| 10417 | - | get_rule_expr(subplan->testexpr, context, showimplicit); | - |
| 10418 | - | appendStringInfoChar(buf, ')'); | - |
| 10419 | - | - | |
| 10420 | - | dpns->ancestors = list_delete_first(dpns->ancestors); | - |
| 10421 | - | } | - |
| 10422 | - | else | - |
| 10423 | - | { | - |
| 10424 | - | const char *nameprefix; | - |
| 10425 | - | - | |
| 10426 | - | /* No referencing Params, so show the SubPlan's name */ | - |
| 10427 | - | if (subplan->isInitPlan) | - |
| 10428 | - | nameprefix = "InitPlan "; | - |
| 10429 | - | else | - |
| 10430 | - | nameprefix = "SubPlan "; | - |
| 10431 | - | if (subplan->useHashTable) | - |
| 10432 | - | appendStringInfo(buf, "hashed %s%s)", | - |
| 10433 | - | nameprefix, subplan->plan_name); | - |
| 10434 | - | else | - |
| 10435 | - | appendStringInfo(buf, "%s%s)", | - |
| 10436 | - | nameprefix, subplan->plan_name); | - |
| 10437 | - | } | - |
| 10438 | - | } | - |
| 10439 | - | break; | - |
| 10440 | - | - | |
| 10441 | - | case T_AlternativeSubPlan: | - |
| 10442 | - | { | - |
| 10443 | - | AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; | - |
| 10444 | - | ListCell *lc; | - |
| 10445 | - | - | |
| 10446 | - | /* | - |
| 10447 | - | * This case cannot be reached in normal usage, since no | - |
| 10448 | - | * AlternativeSubPlan can appear either in parsetrees or | - |
| 10449 | - | * finished plan trees. We keep it just in case somebody | - |
| 10450 | - | * wants to use this code to print planner data structures. | - |
| 10451 | - | */ | - |
| 10452 | - | appendStringInfoString(buf, "(alternatives: "); | - |
| 10453 | - | foreach(lc, asplan->subplans) | - |
| 10454 | - | { | - |
| 10455 | - | SubPlan *splan = lfirst_node(SubPlan, lc); | - |
| 10456 | - | const char *nameprefix; | - |
| 10457 | - | - | |
| 10458 | - | if (splan->isInitPlan) | - |
| 10459 | - | nameprefix = "InitPlan "; | - |
| 10460 | - | else | - |
| 10461 | - | nameprefix = "SubPlan "; | - |
| 10462 | - | if (splan->useHashTable) | - |
| 10463 | - | appendStringInfo(buf, "hashed %s%s", nameprefix, | - |
| 10464 | - | splan->plan_name); | - |
| 10465 | - | else | - |
| 10466 | - | appendStringInfo(buf, "%s%s", nameprefix, | - |
| 10467 | - | splan->plan_name); | - |
| 10468 | - | if (lnext(asplan->subplans, lc)) | - |
| 10469 | - | appendStringInfoString(buf, " or "); | - |
| 10470 | - | } | - |
| 10471 | - | appendStringInfoChar(buf, ')'); | - |
| 10472 | - | } | - |
| 10473 | - | break; | - |
| 10474 | - | - | |
| 10475 | - | case T_FieldSelect: | - |
| 10476 | - | { | - |
| 10477 | - | FieldSelect *fselect = (FieldSelect *) node; | - |
| 10478 | - | Node *arg = (Node *) fselect->arg; | - |
| 10479 | - | int fno = fselect->fieldnum; | - |
| 10480 | - | const char *fieldname; | - |
| 10481 | - | bool need_parens; | - |
| 10482 | - | - | |
| 10483 | - | /* | - |
| 10484 | - | * Parenthesize the argument unless it's a SubscriptingRef or | - |
| 10485 | - | * another FieldSelect. Note in particular that it would be | - |
| 10486 | - | * WRONG to not parenthesize a Var argument; simplicity is not | - |
| 10487 | - | * the issue here, having the right number of names is. | - |
| 10488 | - | */ | - |
| 10489 | - | need_parens = !IsA(arg, SubscriptingRef) && | - |
| 10490 | - | !IsA(arg, FieldSelect); | - |
| 10491 | - | if (need_parens) | - |
| 10492 | - | appendStringInfoChar(buf, '('); | - |
| 10493 | - | get_rule_expr(arg, context, true); | - |
| 10494 | - | if (need_parens) | - |
| 10495 | - | appendStringInfoChar(buf, ')'); | - |
| 10496 | - | - | |
| 10497 | - | /* | - |
| 10498 | - | * Get and print the field name. | - |
| 10499 | - | */ | - |
| 10500 | - | fieldname = get_name_for_var_field((Var *) arg, fno, | - |
| 10501 | - | 0, context); | - |
| 10502 | - | appendStringInfo(buf, ".%s", quote_identifier(fieldname)); | - |
| 10503 | - | } | - |
| 10504 | - | break; | - |
| 10505 | - | - | |
| 10506 | - | case T_FieldStore: | - |
| 10507 | - | { | - |
| 10508 | - | FieldStore *fstore = (FieldStore *) node; | - |
| 10509 | - | bool need_parens; | - |
| 10510 | - | - | |
| 10511 | - | /* | - |
| 10512 | - | * There is no good way to represent a FieldStore as real SQL, | - |
| 10513 | - | * so decompilation of INSERT or UPDATE statements should | - |
| 10514 | - | * always use processIndirection as part of the | - |
| 10515 | - | * statement-level syntax. We should only get here when | - |
| 10516 | - | * EXPLAIN tries to print the targetlist of a plan resulting | - |
| 10517 | - | * from such a statement. The plan case is even harder than | - |
| 10518 | - | * ordinary rules would be, because the planner tries to | - |
| 10519 | - | * collapse multiple assignments to the same field or subfield | - |
| 10520 | - | * into one FieldStore; so we can see a list of target fields | - |
| 10521 | - | * not just one, and the arguments could be FieldStores | - |
| 10522 | - | * themselves. We don't bother to try to print the target | - |
| 10523 | - | * field names; we just print the source arguments, with a | - |
| 10524 | - | * ROW() around them if there's more than one. This isn't | - |
| 10525 | - | * terribly complete, but it's probably good enough for | - |
| 10526 | - | * EXPLAIN's purposes; especially since anything more would be | - |
| 10527 | - | * either hopelessly confusing or an even poorer | - |
| 10528 | - | * representation of what the plan is actually doing. | - |
| 10529 | - | */ | - |
| 10530 | - | need_parens = (list_length(fstore->newvals) != 1); | - |
| 10531 | - | if (need_parens) | - |
| 10532 | - | appendStringInfoString(buf, "ROW("); | - |
| 10533 | - | get_rule_expr((Node *) fstore->newvals, context, showimplicit); | - |
| 10534 | - | if (need_parens) | - |
| 10535 | - | appendStringInfoChar(buf, ')'); | - |
| 10536 | - | } | - |
| 10537 | - | break; | - |
| 10538 | - | - | |
| 10539 | - | case T_RelabelType: | - |
| 10540 | - | { | - |
| 10541 | - | RelabelType *relabel = (RelabelType *) node; | - |
| 10542 | - | Node *arg = (Node *) relabel->arg; | - |
| 10543 | - | - | |
| 10544 | - | if (relabel->relabelformat == COERCE_IMPLICIT_CAST && | - |
| 10545 | - | !showimplicit) | - |
| 10546 | - | { | - |
| 10547 | - | /* don't show the implicit cast */ | - |
| 10548 | - | get_rule_expr_paren(arg, context, false, node); | - |
| 10549 | - | } | - |
| 10550 | - | else | - |
| 10551 | - | { | - |
| 10552 | - | get_coercion_expr(arg, context, | - |
| 10553 | - | relabel->resulttype, | - |
| 10554 | - | relabel->resulttypmod, | - |
| 10555 | - | node); | - |
| 10556 | - | } | - |
| 10557 | - | } | - |
| 10558 | - | break; | - |
| 10559 | - | - | |
| 10560 | - | case T_CoerceViaIO: | - |
| 10561 | - | { | - |
| 10562 | - | CoerceViaIO *iocoerce = (CoerceViaIO *) node; | - |
| 10563 | - | Node *arg = (Node *) iocoerce->arg; | - |
| 10564 | - | - | |
| 10565 | - | if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST && | - |
| 10566 | - | !showimplicit) | - |
| 10567 | - | { | - |
| 10568 | - | /* don't show the implicit cast */ | - |
| 10569 | - | get_rule_expr_paren(arg, context, false, node); | - |
| 10570 | - | } | - |
| 10571 | - | else | - |
| 10572 | - | { | - |
| 10573 | - | get_coercion_expr(arg, context, | - |
| 10574 | - | iocoerce->resulttype, | - |
| 10575 | - | -1, | - |
| 10576 | - | node); | - |
| 10577 | - | } | - |
| 10578 | - | } | - |
| 10579 | - | break; | - |
| 10580 | - | - | |
| 10581 | - | case T_ArrayCoerceExpr: | - |
| 10582 | - | { | - |
| 10583 | - | ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node; | - |
| 10584 | - | Node *arg = (Node *) acoerce->arg; | - |
| 10585 | - | - | |
| 10586 | - | if (acoerce->coerceformat == COERCE_IMPLICIT_CAST && | - |
| 10587 | - | !showimplicit) | - |
| 10588 | - | { | - |
| 10589 | - | /* don't show the implicit cast */ | - |
| 10590 | - | get_rule_expr_paren(arg, context, false, node); | - |
| 10591 | - | } | - |
| 10592 | - | else | - |
| 10593 | - | { | - |
| 10594 | - | get_coercion_expr(arg, context, | - |
| 10595 | - | acoerce->resulttype, | - |
| 10596 | - | acoerce->resulttypmod, | - |
| 10597 | - | node); | - |
| 10598 | - | } | - |
| 10599 | - | } | - |
| 10600 | - | break; | - |
| 10601 | - | - | |
| 10602 | - | case T_ConvertRowtypeExpr: | - |
| 10603 | - | { | - |
| 10604 | - | ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node; | - |
| 10605 | - | Node *arg = (Node *) convert->arg; | - |
| 10606 | - | - | |
| 10607 | - | if (convert->convertformat == COERCE_IMPLICIT_CAST && | - |
| 10608 | - | !showimplicit) | - |
| 10609 | - | { | - |
| 10610 | - | /* don't show the implicit cast */ | - |
| 10611 | - | get_rule_expr_paren(arg, context, false, node); | - |
| 10612 | - | } | - |
| 10613 | - | else | - |
| 10614 | - | { | - |
| 10615 | - | get_coercion_expr(arg, context, | - |
| 10616 | - | convert->resulttype, -1, | - |
| 10617 | - | node); | - |
| 10618 | - | } | - |
| 10619 | - | } | - |
| 10620 | - | break; | - |
| 10621 | - | - | |
| 10622 | - | case T_CollateExpr: | - |
| 10623 | - | { | - |
| 10624 | - | CollateExpr *collate = (CollateExpr *) node; | - |
| 10625 | - | Node *arg = (Node *) collate->arg; | - |
| 10626 | - | - | |
| 10627 | - | if (!PRETTY_PAREN(context)) | - |
| 10628 | - | appendStringInfoChar(buf, '('); | - |
| 10629 | - | get_rule_expr_paren(arg, context, showimplicit, node); | - |
| 10630 | - | appendStringInfo(buf, " COLLATE %s", | - |
| 10631 | - | generate_collation_name(collate->collOid)); | - |
| 10632 | - | if (!PRETTY_PAREN(context)) | - |
| 10633 | - | appendStringInfoChar(buf, ')'); | - |
| 10634 | - | } | - |
| 10635 | - | break; | - |
| 10636 | - | - | |
| 10637 | - | case T_CaseExpr: | - |
| 10638 | - | { | - |
| 10639 | - | CaseExpr *caseexpr = (CaseExpr *) node; | - |
| 10640 | - | ListCell *temp; | - |
| 10641 | - | - | |
| 10642 | - | appendContextKeyword(context, "CASE", | - |
| 10643 | - | 0, PRETTYINDENT_VAR, 0); | - |
| 10644 | - | if (caseexpr->arg) | - |
| 10645 | - | { | - |
| 10646 | - | appendStringInfoChar(buf, ' '); | - |
| 10647 | - | get_rule_expr((Node *) caseexpr->arg, context, true); | - |
| 10648 | - | } | - |
| 10649 | - | foreach(temp, caseexpr->args) | - |
| 10650 | - | { | - |
| 10651 | - | CaseWhen *when = (CaseWhen *) lfirst(temp); | - |
| 10652 | - | Node *w = (Node *) when->expr; | - |
| 10653 | - | - | |
| 10654 | - | if (caseexpr->arg) | - |
| 10655 | - | { | - |
| 10656 | - | /* | - |
| 10657 | - | * The parser should have produced WHEN clauses of the | - |
| 10658 | - | * form "CaseTestExpr = RHS", possibly with an | - |
| 10659 | - | * implicit coercion inserted above the CaseTestExpr. | - |
| 10660 | - | * For accurate decompilation of rules it's essential | - |
| 10661 | - | * that we show just the RHS. However in an | - |
| 10662 | - | * expression that's been through the optimizer, the | - |
| 10663 | - | * WHEN clause could be almost anything (since the | - |
| 10664 | - | * equality operator could have been expanded into an | - |
| 10665 | - | * inline function). If we don't recognize the form | - |
| 10666 | - | * of the WHEN clause, just punt and display it as-is. | - |
| 10667 | - | */ | - |
| 10668 | - | if (IsA(w, OpExpr)) | - |
| 10669 | - | { | - |
| 10670 | - | List *args = ((OpExpr *) w)->args; | - |
| 10671 | - | - | |
| 10672 | - | if (list_length(args) == 2 && | - |
| 10673 | - | IsA(strip_implicit_coercions(linitial(args)), | - |
| 10674 | - | CaseTestExpr)) | - |
| 10675 | - | w = (Node *) lsecond(args); | - |
| 10676 | - | } | - |
| 10677 | - | } | - |
| 10678 | - | - | |
| 10679 | - | if (!PRETTY_INDENT(context)) | - |
| 10680 | - | appendStringInfoChar(buf, ' '); | - |
| 10681 | - | appendContextKeyword(context, "WHEN ", | - |
| 10682 | - | 0, 0, 0); | - |
| 10683 | - | get_rule_expr(w, context, false); | - |
| 10684 | - | appendStringInfoString(buf, " THEN "); | - |
| 10685 | - | get_rule_expr((Node *) when->result, context, true); | - |
| 10686 | - | } | - |
| 10687 | - | if (!PRETTY_INDENT(context)) | - |
| 10688 | - | appendStringInfoChar(buf, ' '); | - |
| 10689 | - | appendContextKeyword(context, "ELSE ", | - |
| 10690 | - | 0, 0, 0); | - |
| 10691 | - | get_rule_expr((Node *) caseexpr->defresult, context, true); | - |
| 10692 | - | if (!PRETTY_INDENT(context)) | - |
| 10693 | - | appendStringInfoChar(buf, ' '); | - |
| 10694 | - | appendContextKeyword(context, "END", | - |
| 10695 | - | -PRETTYINDENT_VAR, 0, 0); | - |
| 10696 | - | } | - |
| 10697 | - | break; | - |
| 10698 | - | - | |
| 10699 | - | case T_CaseTestExpr: | - |
| 10700 | - | { | - |
| 10701 | - | /* | - |
| 10702 | - | * Normally we should never get here, since for expressions | - |
| 10703 | - | * that can contain this node type we attempt to avoid | - |
| 10704 | - | * recursing to it. But in an optimized expression we might | - |
| 10705 | - | * be unable to avoid that (see comments for CaseExpr). If we | - |
| 10706 | - | * do see one, print it as CASE_TEST_EXPR. | - |
| 10707 | - | */ | - |
| 10708 | - | appendStringInfoString(buf, "CASE_TEST_EXPR"); | - |
| 10709 | - | } | - |
| 10710 | - | break; | - |
| 10711 | - | - | |
| 10712 | - | case T_ArrayExpr: | - |
| 10713 | - | { | - |
| 10714 | - | ArrayExpr *arrayexpr = (ArrayExpr *) node; | - |
| 10715 | - | - | |
| 10716 | - | appendStringInfoString(buf, "ARRAY["); | - |
| 10717 | - | get_rule_expr((Node *) arrayexpr->elements, context, true); | - |
| 10718 | - | appendStringInfoChar(buf, ']'); | - |
| 10719 | - | - | |
| 10720 | - | /* | - |
| 10721 | - | * If the array isn't empty, we assume its elements are | - |
| 10722 | - | * coerced to the desired type. If it's empty, though, we | - |
| 10723 | - | * need an explicit coercion to the array type. | - |
| 10724 | - | */ | - |
| 10725 | - | if (arrayexpr->elements == NIL) | - |
| 10726 | - | appendStringInfo(buf, "::%s", | - |
| 10727 | - | format_type_with_typemod(arrayexpr->array_typeid, -1)); | - |
| 10728 | - | } | - |
| 10729 | - | break; | - |
| 10730 | - | - | |
| 10731 | - | case T_RowExpr: | - |
| 10732 | - | { | - |
| 10733 | - | RowExpr *rowexpr = (RowExpr *) node; | - |
| 10734 | - | TupleDesc tupdesc = NULL; | - |
| 10735 | - | ListCell *arg; | - |
| 10736 | - | int i; | - |
| 10737 | - | char *sep; | - |
| 10738 | - | - | |
| 10739 | - | /* | - |
| 10740 | - | * If it's a named type and not RECORD, we may have to skip | - |
| 10741 | - | * dropped columns and/or claim there are NULLs for added | - |
| 10742 | - | * columns. | - |
| 10743 | - | */ | - |
| 10744 | - | if (rowexpr->row_typeid != RECORDOID) | - |
| 10745 | - | { | - |
| 10746 | - | tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); | - |
| 10747 | - | Assert(list_length(rowexpr->args) <= tupdesc->natts); | - |
| 10748 | - | } | - |
| 10749 | - | - | |
| 10750 | - | /* | - |
| 10751 | - | * SQL99 allows "ROW" to be omitted when there is more than | - |
| 10752 | - | * one column, but for simplicity we always print it. | - |
| 10753 | - | */ | - |
| 10754 | - | appendStringInfoString(buf, "ROW("); | - |
| 10755 | - | sep = ""; | - |
| 10756 | - | i = 0; | - |
| 10757 | - | foreach(arg, rowexpr->args) | - |
| 10758 | - | { | - |
| 10759 | - | Node *e = (Node *) lfirst(arg); | - |
| 10760 | - | - | |
| 10761 | - | if (tupdesc == NULL || | - |
| 10762 | - | !TupleDescCompactAttr(tupdesc, i)->attisdropped) | - |
| 10763 | - | { | - |
| 10764 | - | appendStringInfoString(buf, sep); | - |
| 10765 | - | /* Whole-row Vars need special treatment here */ | - |
| 10766 | - | get_rule_expr_toplevel(e, context, true); | - |
| 10767 | - | sep = ", "; | - |
| 10768 | - | } | - |
| 10769 | - | i++; | - |
| 10770 | - | } | - |
| 10771 | - | if (tupdesc != NULL) | - |
| 10772 | - | { | - |
| 10773 | - | while (i < tupdesc->natts) | - |
| 10774 | - | { | - |
| 10775 | - | if (!TupleDescCompactAttr(tupdesc, i)->attisdropped) | - |
| 10776 | - | { | - |
| 10777 | - | appendStringInfoString(buf, sep); | - |
| 10778 | - | appendStringInfoString(buf, "NULL"); | - |
| 10779 | - | sep = ", "; | - |
| 10780 | - | } | - |
| 10781 | - | i++; | - |
| 10782 | - | } | - |
| 10783 | - | - | |
| 10784 | - | ReleaseTupleDesc(tupdesc); | - |
| 10785 | - | } | - |
| 10786 | - | appendStringInfoChar(buf, ')'); | - |
| 10787 | - | if (rowexpr->row_format == COERCE_EXPLICIT_CAST) | - |
| 10788 | - | appendStringInfo(buf, "::%s", | - |
| 10789 | - | format_type_with_typemod(rowexpr->row_typeid, -1)); | - |
| 10790 | - | } | - |
| 10791 | - | break; | - |
| 10792 | - | - | |
| 10793 | - | case T_RowCompareExpr: | - |
| 10794 | - | { | - |
| 10795 | - | RowCompareExpr *rcexpr = (RowCompareExpr *) node; | - |
| 10796 | - | - | |
| 10797 | - | /* | - |
| 10798 | - | * SQL99 allows "ROW" to be omitted when there is more than | - |
| 10799 | - | * one column, but for simplicity we always print it. Within | - |
| 10800 | - | * a ROW expression, whole-row Vars need special treatment, so | - |
| 10801 | - | * use get_rule_list_toplevel. | - |
| 10802 | - | */ | - |
| 10803 | - | appendStringInfoString(buf, "(ROW("); | - |
| 10804 | - | get_rule_list_toplevel(rcexpr->largs, context, true); | - |
| 10805 | - | - | |
| 10806 | - | /* | - |
| 10807 | - | * We assume that the name of the first-column operator will | - |
| 10808 | - | * do for all the rest too. This is definitely open to | - |
| 10809 | - | * failure, eg if some but not all operators were renamed | - |
| 10810 | - | * since the construct was parsed, but there seems no way to | - |
| 10811 | - | * be perfect. | - |
| 10812 | - | */ | - |
| 10813 | - | appendStringInfo(buf, ") %s ROW(", | - |
| 10814 | - | generate_operator_name(linitial_oid(rcexpr->opnos), | - |
| 10815 | - | exprType(linitial(rcexpr->largs)), | - |
| 10816 | - | exprType(linitial(rcexpr->rargs)))); | - |
| 10817 | - | get_rule_list_toplevel(rcexpr->rargs, context, true); | - |
| 10818 | - | appendStringInfoString(buf, "))"); | - |
| 10819 | - | } | - |
| 10820 | - | break; | - |
| 10821 | - | - | |
| 10822 | - | case T_CoalesceExpr: | - |
| 10823 | - | { | - |
| 10824 | - | CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; | - |
| 10825 | - | - | |
| 10826 | - | appendStringInfoString(buf, "COALESCE("); | - |
| 10827 | - | get_rule_expr((Node *) coalesceexpr->args, context, true); | - |
| 10828 | - | appendStringInfoChar(buf, ')'); | - |
| 10829 | - | } | - |
| 10830 | - | break; | - |
| 10831 | - | - | |
| 10832 | - | case T_MinMaxExpr: | - |
| 10833 | - | { | - |
| 10834 | - | MinMaxExpr *minmaxexpr = (MinMaxExpr *) node; | - |
| 10835 | - | - | |
| 10836 | - | switch (minmaxexpr->op) | - |
| 10837 | - | { | - |
| 10838 | - | case IS_GREATEST: | - |
| 10839 | - | appendStringInfoString(buf, "GREATEST("); | - |
| 10840 | - | break; | - |
| 10841 | - | case IS_LEAST: | - |
| 10842 | - | appendStringInfoString(buf, "LEAST("); | - |
| 10843 | - | break; | - |
| 10844 | - | } | - |
| 10845 | - | get_rule_expr((Node *) minmaxexpr->args, context, true); | - |
| 10846 | - | appendStringInfoChar(buf, ')'); | - |
| 10847 | - | } | - |
| 10848 | - | break; | - |
| 10849 | - | - | |
| 10850 | - | case T_SQLValueFunction: | - |
| 10851 | - | { | - |
| 10852 | - | SQLValueFunction *svf = (SQLValueFunction *) node; | - |
| 10853 | - | - | |
| 10854 | - | /* | - |
| 10855 | - | * Note: this code knows that typmod for time, timestamp, and | - |
| 10856 | - | * timestamptz just prints as integer. | - |
| 10857 | - | */ | - |
| 10858 | - | switch (svf->op) | - |
| 10859 | - | { | - |
| 10860 | - | case SVFOP_CURRENT_DATE: | - |
| 10861 | - | appendStringInfoString(buf, "CURRENT_DATE"); | - |
| 10862 | - | break; | - |
| 10863 | - | case SVFOP_CURRENT_TIME: | - |
| 10864 | - | appendStringInfoString(buf, "CURRENT_TIME"); | - |
| 10865 | - | break; | - |
| 10866 | - | case SVFOP_CURRENT_TIME_N: | - |
| 10867 | - | appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod); | - |
| 10868 | - | break; | - |
| 10869 | - | case SVFOP_CURRENT_TIMESTAMP: | - |
| 10870 | - | appendStringInfoString(buf, "CURRENT_TIMESTAMP"); | - |
| 10871 | - | break; | - |
| 10872 | - | case SVFOP_CURRENT_TIMESTAMP_N: | - |
| 10873 | - | appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)", | - |
| 10874 | - | svf->typmod); | - |
| 10875 | - | break; | - |
| 10876 | - | case SVFOP_LOCALTIME: | - |
| 10877 | - | appendStringInfoString(buf, "LOCALTIME"); | - |
| 10878 | - | break; | - |
| 10879 | - | case SVFOP_LOCALTIME_N: | - |
| 10880 | - | appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod); | - |
| 10881 | - | break; | - |
| 10882 | - | case SVFOP_LOCALTIMESTAMP: | - |
| 10883 | - | appendStringInfoString(buf, "LOCALTIMESTAMP"); | - |
| 10884 | - | break; | - |
| 10885 | - | case SVFOP_LOCALTIMESTAMP_N: | - |
| 10886 | - | appendStringInfo(buf, "LOCALTIMESTAMP(%d)", | - |
| 10887 | - | svf->typmod); | - |
| 10888 | - | break; | - |
| 10889 | - | case SVFOP_CURRENT_ROLE: | - |
| 10890 | - | appendStringInfoString(buf, "CURRENT_ROLE"); | - |
| 10891 | - | break; | - |
| 10892 | - | case SVFOP_CURRENT_USER: | - |
| 10893 | - | appendStringInfoString(buf, "CURRENT_USER"); | - |
| 10894 | - | break; | - |
| 10895 | - | case SVFOP_USER: | - |
| 10896 | - | appendStringInfoString(buf, "USER"); | - |
| 10897 | - | break; | - |
| 10898 | - | case SVFOP_SESSION_USER: | - |
| 10899 | - | appendStringInfoString(buf, "SESSION_USER"); | - |
| 10900 | - | break; | - |
| 10901 | - | case SVFOP_CURRENT_CATALOG: | - |
| 10902 | - | appendStringInfoString(buf, "CURRENT_CATALOG"); | - |
| 10903 | - | break; | - |
| 10904 | - | case SVFOP_CURRENT_SCHEMA: | - |
| 10905 | - | appendStringInfoString(buf, "CURRENT_SCHEMA"); | - |
| 10906 | - | break; | - |
| 10907 | - | } | - |
| 10908 | - | } | - |
| 10909 | - | break; | - |
| 10910 | - | - | |
| 10911 | - | case T_XmlExpr: | - |
| 10912 | - | { | - |
| 10913 | - | XmlExpr *xexpr = (XmlExpr *) node; | - |
| 10914 | - | bool needcomma = false; | - |
| 10915 | - | ListCell *arg; | - |
| 10916 | - | ListCell *narg; | - |
| 10917 | - | Const *con; | - |
| 10918 | - | - | |
| 10919 | - | switch (xexpr->op) | - |
| 10920 | - | { | - |
| 10921 | - | case IS_XMLCONCAT: | - |
| 10922 | - | appendStringInfoString(buf, "XMLCONCAT("); | - |
| 10923 | - | break; | - |
| 10924 | - | case IS_XMLELEMENT: | - |
| 10925 | - | appendStringInfoString(buf, "XMLELEMENT("); | - |
| 10926 | - | break; | - |
| 10927 | - | case IS_XMLFOREST: | - |
| 10928 | - | appendStringInfoString(buf, "XMLFOREST("); | - |
| 10929 | - | break; | - |
| 10930 | - | case IS_XMLPARSE: | - |
| 10931 | - | appendStringInfoString(buf, "XMLPARSE("); | - |
| 10932 | - | break; | - |
| 10933 | - | case IS_XMLPI: | - |
| 10934 | - | appendStringInfoString(buf, "XMLPI("); | - |
| 10935 | - | break; | - |
| 10936 | - | case IS_XMLROOT: | - |
| 10937 | - | appendStringInfoString(buf, "XMLROOT("); | - |
| 10938 | - | break; | - |
| 10939 | - | case IS_XMLSERIALIZE: | - |
| 10940 | - | appendStringInfoString(buf, "XMLSERIALIZE("); | - |
| 10941 | - | break; | - |
| 10942 | - | case IS_DOCUMENT: | - |
| 10943 | - | break; | - |
| 10944 | - | } | - |
| 10945 | - | if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE) | - |
| 10946 | - | { | - |
| 10947 | - | if (xexpr->xmloption == XMLOPTION_DOCUMENT) | - |
| 10948 | - | appendStringInfoString(buf, "DOCUMENT "); | - |
| 10949 | - | else | - |
| 10950 | - | appendStringInfoString(buf, "CONTENT "); | - |
| 10951 | - | } | - |
| 10952 | - | if (xexpr->name) | - |
| 10953 | - | { | - |
| 10954 | - | appendStringInfo(buf, "NAME %s", | - |
| 10955 | - | quote_identifier(map_xml_name_to_sql_identifier(xexpr->name))); | - |
| 10956 | - | needcomma = true; | - |
| 10957 | - | } | - |
| 10958 | - | if (xexpr->named_args) | - |
| 10959 | - | { | - |
| 10960 | - | if (xexpr->op != IS_XMLFOREST) | - |
| 10961 | - | { | - |
| 10962 | - | if (needcomma) | - |
| 10963 | - | appendStringInfoString(buf, ", "); | - |
| 10964 | - | appendStringInfoString(buf, "XMLATTRIBUTES("); | - |
| 10965 | - | needcomma = false; | - |
| 10966 | - | } | - |
| 10967 | - | forboth(arg, xexpr->named_args, narg, xexpr->arg_names) | - |
| 10968 | - | { | - |
| 10969 | - | Node *e = (Node *) lfirst(arg); | - |
| 10970 | - | char *argname = strVal(lfirst(narg)); | - |
| 10971 | - | - | |
| 10972 | - | if (needcomma) | - |
| 10973 | - | appendStringInfoString(buf, ", "); | - |
| 10974 | - | get_rule_expr(e, context, true); | - |
| 10975 | - | appendStringInfo(buf, " AS %s", | - |
| 10976 | - | quote_identifier(map_xml_name_to_sql_identifier(argname))); | - |
| 10977 | - | needcomma = true; | - |
| 10978 | - | } | - |
| 10979 | - | if (xexpr->op != IS_XMLFOREST) | - |
| 10980 | - | appendStringInfoChar(buf, ')'); | - |
| 10981 | - | } | - |
| 10982 | - | if (xexpr->args) | - |
| 10983 | - | { | - |
| 10984 | - | if (needcomma) | - |
| 10985 | - | appendStringInfoString(buf, ", "); | - |
| 10986 | - | switch (xexpr->op) | - |
| 10987 | - | { | - |
| 10988 | - | case IS_XMLCONCAT: | - |
| 10989 | - | case IS_XMLELEMENT: | - |
| 10990 | - | case IS_XMLFOREST: | - |
| 10991 | - | case IS_XMLPI: | - |
| 10992 | - | case IS_XMLSERIALIZE: | - |
| 10993 | - | /* no extra decoration needed */ | - |
| 10994 | - | get_rule_expr((Node *) xexpr->args, context, true); | - |
| 10995 | - | break; | - |
| 10996 | - | case IS_XMLPARSE: | - |
| 10997 | - | Assert(list_length(xexpr->args) == 2); | - |
| 10998 | - | - | |
| 10999 | - | get_rule_expr((Node *) linitial(xexpr->args), | - |
| 11000 | - | context, true); | - |
| 11001 | - | - | |
| 11002 | - | con = lsecond_node(Const, xexpr->args); | - |
| 11003 | - | Assert(!con->constisnull); | - |
| 11004 | - | if (DatumGetBool(con->constvalue)) | - |
| 11005 | - | appendStringInfoString(buf, | - |
| 11006 | - | " PRESERVE WHITESPACE"); | - |
| 11007 | - | else | - |
| 11008 | - | appendStringInfoString(buf, | - |
| 11009 | - | " STRIP WHITESPACE"); | - |
| 11010 | - | break; | - |
| 11011 | - | case IS_XMLROOT: | - |
| 11012 | - | Assert(list_length(xexpr->args) == 3); | - |
| 11013 | - | - | |
| 11014 | - | get_rule_expr((Node *) linitial(xexpr->args), | - |
| 11015 | - | context, true); | - |
| 11016 | - | - | |
| 11017 | - | appendStringInfoString(buf, ", VERSION "); | - |
| 11018 | - | con = (Const *) lsecond(xexpr->args); | - |
| 11019 | - | if (IsA(con, Const) && | - |
| 11020 | - | con->constisnull) | - |
| 11021 | - | appendStringInfoString(buf, "NO VALUE"); | - |
| 11022 | - | else | - |
| 11023 | - | get_rule_expr((Node *) con, context, false); | - |
| 11024 | - | - | |
| 11025 | - | con = lthird_node(Const, xexpr->args); | - |
| 11026 | - | if (con->constisnull) | - |
| 11027 | - | /* suppress STANDALONE NO VALUE */ ; | - |
| 11028 | - | else | - |
| 11029 | - | { | - |
| 11030 | - | switch (DatumGetInt32(con->constvalue)) | - |
| 11031 | - | { | - |
| 11032 | - | case XML_STANDALONE_YES: | - |
| 11033 | - | appendStringInfoString(buf, | - |
| 11034 | - | ", STANDALONE YES"); | - |
| 11035 | - | break; | - |
| 11036 | - | case XML_STANDALONE_NO: | - |
| 11037 | - | appendStringInfoString(buf, | - |
| 11038 | - | ", STANDALONE NO"); | - |
| 11039 | - | break; | - |
| 11040 | - | case XML_STANDALONE_NO_VALUE: | - |
| 11041 | - | appendStringInfoString(buf, | - |
| 11042 | - | ", STANDALONE NO VALUE"); | - |
| 11043 | - | break; | - |
| 11044 | - | default: | - |
| 11045 | - | break; | - |
| 11046 | - | } | - |
| 11047 | - | } | - |
| 11048 | - | break; | - |
| 11049 | - | case IS_DOCUMENT: | - |
| 11050 | - | get_rule_expr_paren((Node *) xexpr->args, context, false, node); | - |
| 11051 | - | break; | - |
| 11052 | - | } | - |
| 11053 | - | } | - |
| 11054 | - | if (xexpr->op == IS_XMLSERIALIZE) | - |
| 11055 | - | { | - |
| 11056 | - | appendStringInfo(buf, " AS %s", | - |
| 11057 | - | format_type_with_typemod(xexpr->type, | - |
| 11058 | - | xexpr->typmod)); | - |
| 11059 | - | if (xexpr->indent) | - |
| 11060 | - | appendStringInfoString(buf, " INDENT"); | - |
| 11061 | - | else | - |
| 11062 | - | appendStringInfoString(buf, " NO INDENT"); | - |
| 11063 | - | } | - |
| 11064 | - | - | |
| 11065 | - | if (xexpr->op == IS_DOCUMENT) | - |
| 11066 | - | appendStringInfoString(buf, " IS DOCUMENT"); | - |
| 11067 | - | else | - |
| 11068 | - | appendStringInfoChar(buf, ')'); | - |
| 11069 | - | } | - |
| 11070 | - | break; | - |
| 11071 | - | - | |
| 11072 | - | case T_NullTest: | - |
| 11073 | - | { | - |
| 11074 | - | NullTest *ntest = (NullTest *) node; | - |
| 11075 | - | - | |
| 11076 | - | if (!PRETTY_PAREN(context)) | - |
| 11077 | - | appendStringInfoChar(buf, '('); | - |
| 11078 | - | get_rule_expr_paren((Node *) ntest->arg, context, true, node); | - |
| 11079 | - | - | |
| 11080 | - | /* | - |
| 11081 | - | * For scalar inputs, we prefer to print as IS [NOT] NULL, | - |
| 11082 | - | * which is shorter and traditional. If it's a rowtype input | - |
| 11083 | - | * but we're applying a scalar test, must print IS [NOT] | - |
| 11084 | - | * DISTINCT FROM NULL to be semantically correct. | - |
| 11085 | - | */ | - |
| 11086 | - | if (ntest->argisrow || | - |
| 11087 | - | !type_is_rowtype(exprType((Node *) ntest->arg))) | - |
| 11088 | - | { | - |
| 11089 | - | switch (ntest->nulltesttype) | - |
| 11090 | - | { | - |
| 11091 | - | case IS_NULL: | - |
| 11092 | - | appendStringInfoString(buf, " IS NULL"); | - |
| 11093 | - | break; | - |
| 11094 | - | case IS_NOT_NULL: | - |
| 11095 | - | appendStringInfoString(buf, " IS NOT NULL"); | - |
| 11096 | - | break; | - |
| 11097 | - | default: | - |
| 11098 | - | elog(ERROR, "unrecognized nulltesttype: %d", | - |
| 11099 | - | (int) ntest->nulltesttype); | - |
| 11100 | - | } | - |
| 11101 | - | } | - |
| 11102 | - | else | - |
| 11103 | - | { | - |
| 11104 | - | switch (ntest->nulltesttype) | - |
| 11105 | - | { | - |
| 11106 | - | case IS_NULL: | - |
| 11107 | - | appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL"); | - |
| 11108 | - | break; | - |
| 11109 | - | case IS_NOT_NULL: | - |
| 11110 | - | appendStringInfoString(buf, " IS DISTINCT FROM NULL"); | - |
| 11111 | - | break; | - |
| 11112 | - | default: | - |
| 11113 | - | elog(ERROR, "unrecognized nulltesttype: %d", | - |
| 11114 | - | (int) ntest->nulltesttype); | - |
| 11115 | - | } | - |
| 11116 | - | } | - |
| 11117 | - | if (!PRETTY_PAREN(context)) | - |
| 11118 | - | appendStringInfoChar(buf, ')'); | - |
| 11119 | - | } | - |
| 11120 | - | break; | - |
| 11121 | - | - | |
| 11122 | - | case T_BooleanTest: | - |
| 11123 | - | { | - |
| 11124 | - | BooleanTest *btest = (BooleanTest *) node; | - |
| 11125 | - | - | |
| 11126 | - | if (!PRETTY_PAREN(context)) | - |
| 11127 | - | appendStringInfoChar(buf, '('); | - |
| 11128 | - | get_rule_expr_paren((Node *) btest->arg, context, false, node); | - |
| 11129 | - | switch (btest->booltesttype) | - |
| 11130 | - | { | - |
| 11131 | - | case IS_TRUE: | - |
| 11132 | - | appendStringInfoString(buf, " IS TRUE"); | - |
| 11133 | - | break; | - |
| 11134 | - | case IS_NOT_TRUE: | - |
| 11135 | - | appendStringInfoString(buf, " IS NOT TRUE"); | - |
| 11136 | - | break; | - |
| 11137 | - | case IS_FALSE: | - |
| 11138 | - | appendStringInfoString(buf, " IS FALSE"); | - |
| 11139 | - | break; | - |
| 11140 | - | case IS_NOT_FALSE: | - |
| 11141 | - | appendStringInfoString(buf, " IS NOT FALSE"); | - |
| 11142 | - | break; | - |
| 11143 | - | case IS_UNKNOWN: | - |
| 11144 | - | appendStringInfoString(buf, " IS UNKNOWN"); | - |
| 11145 | - | break; | - |
| 11146 | - | case IS_NOT_UNKNOWN: | - |
| 11147 | - | appendStringInfoString(buf, " IS NOT UNKNOWN"); | - |
| 11148 | - | break; | - |
| 11149 | - | default: | - |
| 11150 | - | elog(ERROR, "unrecognized booltesttype: %d", | - |
| 11151 | - | (int) btest->booltesttype); | - |
| 11152 | - | } | - |
| 11153 | - | if (!PRETTY_PAREN(context)) | - |
| 11154 | - | appendStringInfoChar(buf, ')'); | - |
| 11155 | - | } | - |
| 11156 | - | break; | - |
| 11157 | - | - | |
| 11158 | - | case T_CoerceToDomain: | - |
| 11159 | - | { | - |
| 11160 | - | CoerceToDomain *ctest = (CoerceToDomain *) node; | - |
| 11161 | - | Node *arg = (Node *) ctest->arg; | - |
| 11162 | - | - | |
| 11163 | - | if (ctest->coercionformat == COERCE_IMPLICIT_CAST && | - |
| 11164 | - | !showimplicit) | - |
| 11165 | - | { | - |
| 11166 | - | /* don't show the implicit cast */ | - |
| 11167 | - | get_rule_expr(arg, context, false); | - |
| 11168 | - | } | - |
| 11169 | - | else | - |
| 11170 | - | { | - |
| 11171 | - | get_coercion_expr(arg, context, | - |
| 11172 | - | ctest->resulttype, | - |
| 11173 | - | ctest->resulttypmod, | - |
| 11174 | - | node); | - |
| 11175 | - | } | - |
| 11176 | - | } | - |
| 11177 | - | break; | - |
| 11178 | - | - | |
| 11179 | - | case T_CoerceToDomainValue: | - |
| 11180 | - | appendStringInfoString(buf, "VALUE"); | - |
| 11181 | - | break; | - |
| 11182 | - | - | |
| 11183 | - | case T_SetToDefault: | - |
| 11184 | - | appendStringInfoString(buf, "DEFAULT"); | - |
| 11185 | - | break; | - |
| 11186 | - | - | |
| 11187 | - | case T_CurrentOfExpr: | - |
| 11188 | - | { | - |
| 11189 | - | CurrentOfExpr *cexpr = (CurrentOfExpr *) node; | - |
| 11190 | - | - | |
| 11191 | - | if (cexpr->cursor_name) | - |
| 11192 | - | appendStringInfo(buf, "CURRENT OF %s", | - |
| 11193 | - | quote_identifier(cexpr->cursor_name)); | - |
| 11194 | - | else | - |
| 11195 | - | appendStringInfo(buf, "CURRENT OF $%d", | - |
| 11196 | - | cexpr->cursor_param); | - |
| 11197 | - | } | - |
| 11198 | - | break; | - |
| 11199 | - | - | |
| 11200 | - | case T_NextValueExpr: | - |
| 11201 | - | { | - |
| 11202 | - | NextValueExpr *nvexpr = (NextValueExpr *) node; | - |
| 11203 | - | - | |
| 11204 | - | /* | - |
| 11205 | - | * This isn't exactly nextval(), but that seems close enough | - |
| 11206 | - | * for EXPLAIN's purposes. | - |
| 11207 | - | */ | - |
| 11208 | - | appendStringInfoString(buf, "nextval("); | - |
| 11209 | - | simple_quote_literal(buf, | - |
| 11210 | - | generate_relation_name(nvexpr->seqid, | - |
| 11211 | - | NIL)); | - |
| 11212 | - | appendStringInfoChar(buf, ')'); | - |
| 11213 | - | } | - |
| 11214 | - | break; | - |
| 11215 | - | - | |
| 11216 | - | case T_InferenceElem: | - |
| 11217 | - | { | - |
| 11218 | - | InferenceElem *iexpr = (InferenceElem *) node; | - |
| 11219 | - | bool save_varprefix; | - |
| 11220 | - | bool need_parens; | - |
| 11221 | - | - | |
| 11222 | - | /* | - |
| 11223 | - | * InferenceElem can only refer to target relation, so a | - |
| 11224 | - | * prefix is not useful, and indeed would cause parse errors. | - |
| 11225 | - | */ | - |
| 11226 | - | save_varprefix = context->varprefix; | - |
| 11227 | - | context->varprefix = false; | - |
| 11228 | - | - | |
| 11229 | - | /* | - |
| 11230 | - | * Parenthesize the element unless it's a simple Var or a bare | - |
| 11231 | - | * function call. Follows pg_get_indexdef_worker(). | - |
| 11232 | - | */ | - |
| 11233 | - | need_parens = !IsA(iexpr->expr, Var); | - |
| 11234 | - | if (IsA(iexpr->expr, FuncExpr) && | - |
| 11235 | - | ((FuncExpr *) iexpr->expr)->funcformat == | - |
| 11236 | - | COERCE_EXPLICIT_CALL) | - |
| 11237 | - | need_parens = false; | - |
| 11238 | - | - | |
| 11239 | - | if (need_parens) | - |
| 11240 | - | appendStringInfoChar(buf, '('); | - |
| 11241 | - | get_rule_expr((Node *) iexpr->expr, | - |
| 11242 | - | context, false); | - |
| 11243 | - | if (need_parens) | - |
| 11244 | - | appendStringInfoChar(buf, ')'); | - |
| 11245 | - | - | |
| 11246 | - | context->varprefix = save_varprefix; | - |
| 11247 | - | - | |
| 11248 | - | if (iexpr->infercollid) | - |
| 11249 | - | appendStringInfo(buf, " COLLATE %s", | - |
| 11250 | - | generate_collation_name(iexpr->infercollid)); | - |
| 11251 | - | - | |
| 11252 | - | /* Add the operator class name, if not default */ | - |
| 11253 | - | if (iexpr->inferopclass) | - |
| 11254 | - | { | - |
| 11255 | - | Oid inferopclass = iexpr->inferopclass; | - |
| 11256 | - | Oid inferopcinputtype = get_opclass_input_type(iexpr->inferopclass); | - |
| 11257 | - | - | |
| 11258 | - | get_opclass_name(inferopclass, inferopcinputtype, buf); | - |
| 11259 | - | } | - |
| 11260 | - | } | - |
| 11261 | - | break; | - |
| 11262 | - | - | |
| 11263 | - | case T_ReturningExpr: | - |
| 11264 | - | { | - |
| 11265 | - | ReturningExpr *retExpr = (ReturningExpr *) node; | - |
| 11266 | - | - | |
| 11267 | - | /* | - |
| 11268 | - | * We cannot see a ReturningExpr in rule deparsing, only while | - |
| 11269 | - | * EXPLAINing a query plan (ReturningExpr nodes are only ever | - |
| 11270 | - | * adding during query rewriting). Just display the expression | - |
| 11271 | - | * returned (an expanded view column). | - |
| 11272 | - | */ | - |
| 11273 | - | get_rule_expr((Node *) retExpr->retexpr, context, showimplicit); | - |
| 11274 | - | } | - |
| 11275 | - | break; | - |
| 11276 | - | - | |
| 11277 | - | case T_PartitionBoundSpec: | - |
| 11278 | - | { | - |
| 11279 | - | PartitionBoundSpec *spec = (PartitionBoundSpec *) node; | - |
| 11280 | - | ListCell *cell; | - |
| 11281 | - | char *sep; | - |
| 11282 | - | - | |
| 11283 | - | if (spec->is_default) | - |
| 11284 | - | { | - |
| 11285 | - | appendStringInfoString(buf, "DEFAULT"); | - |
| 11286 | - | break; | - |
| 11287 | - | } | - |
| 11288 | - | - | |
| 11289 | - | switch (spec->strategy) | - |
| 11290 | - | { | - |
| 11291 | - | case PARTITION_STRATEGY_HASH: | - |
| 11292 | - | Assert(spec->modulus > 0 && spec->remainder >= 0); | - |
| 11293 | - | Assert(spec->modulus > spec->remainder); | - |
| 11294 | - | - | |
| 11295 | - | appendStringInfoString(buf, "FOR VALUES"); | - |
| 11296 | - | appendStringInfo(buf, " WITH (modulus %d, remainder %d)", | - |
| 11297 | - | spec->modulus, spec->remainder); | - |
| 11298 | - | break; | - |
| 11299 | - | - | |
| 11300 | - | case PARTITION_STRATEGY_LIST: | - |
| 11301 | - | Assert(spec->listdatums != NIL); | - |
| 11302 | - | - | |
| 11303 | - | appendStringInfoString(buf, "FOR VALUES IN ("); | - |
| 11304 | - | sep = ""; | - |
| 11305 | - | foreach(cell, spec->listdatums) | - |
| 11306 | - | { | - |
| 11307 | - | Const *val = lfirst_node(Const, cell); | - |
| 11308 | - | - | |
| 11309 | - | appendStringInfoString(buf, sep); | - |
| 11310 | - | get_const_expr(val, context, -1); | - |
| 11311 | - | sep = ", "; | - |
| 11312 | - | } | - |
| 11313 | - | - | |
| 11314 | - | appendStringInfoChar(buf, ')'); | - |
| 11315 | - | break; | - |
| 11316 | - | - | |
| 11317 | - | case PARTITION_STRATEGY_RANGE: | - |
| 11318 | - | Assert(spec->lowerdatums != NIL && | - |
| 11319 | - | spec->upperdatums != NIL && | - |
| 11320 | - | list_length(spec->lowerdatums) == | - |
| 11321 | - | list_length(spec->upperdatums)); | - |
| 11322 | - | - | |
| 11323 | - | appendStringInfo(buf, "FOR VALUES FROM %s TO %s", | - |
| 11324 | - | get_range_partbound_string(spec->lowerdatums), | - |
| 11325 | - | get_range_partbound_string(spec->upperdatums)); | - |
| 11326 | - | break; | - |
| 11327 | - | - | |
| 11328 | - | default: | - |
| 11329 | - | elog(ERROR, "unrecognized partition strategy: %d", | - |
| 11330 | - | (int) spec->strategy); | - |
| 11331 | - | break; | - |
| 11332 | - | } | - |
| 11333 | - | } | - |
| 11334 | - | break; | - |
| 11335 | - | - | |
| 11336 | - | case T_JsonValueExpr: | - |
| 11337 | - | { | - |
| 11338 | - | JsonValueExpr *jve = (JsonValueExpr *) node; | - |
| 11339 | - | - | |
| 11340 | - | get_rule_expr((Node *) jve->raw_expr, context, false); | - |
| 11341 | - | get_json_format(jve->format, context->buf); | - |
| 11342 | - | } | - |
| 11343 | - | break; | - |
| 11344 | - | - | |
| 11345 | - | case T_JsonConstructorExpr: | - |
| 11346 | - | get_json_constructor((JsonConstructorExpr *) node, context, false); | - |
| 11347 | - | break; | - |
| 11348 | - | - | |
| 11349 | - | case T_JsonIsPredicate: | - |
| 11350 | - | { | - |
| 11351 | - | JsonIsPredicate *pred = (JsonIsPredicate *) node; | - |
| 11352 | - | - | |
| 11353 | - | if (!PRETTY_PAREN(context)) | - |
| 11354 | - | appendStringInfoChar(context->buf, '('); | - |
| 11355 | - | - | |
| 11356 | - | get_rule_expr_paren(pred->expr, context, true, node); | - |
| 11357 | - | - | |
| 11358 | - | appendStringInfoString(context->buf, " IS JSON"); | - |
| 11359 | - | - | |
| 11360 | - | /* TODO: handle FORMAT clause */ | - |
| 11361 | - | - | |
| 11362 | - | switch (pred->item_type) | - |
| 11363 | - | { | - |
| 11364 | - | case JS_TYPE_SCALAR: | - |
| 11365 | - | appendStringInfoString(context->buf, " SCALAR"); | - |
| 11366 | - | break; | - |
| 11367 | - | case JS_TYPE_ARRAY: | - |
| 11368 | - | appendStringInfoString(context->buf, " ARRAY"); | - |
| 11369 | - | break; | - |
| 11370 | - | case JS_TYPE_OBJECT: | - |
| 11371 | - | appendStringInfoString(context->buf, " OBJECT"); | - |
| 11372 | - | break; | - |
| 11373 | - | default: | - |
| 11374 | - | break; | - |
| 11375 | - | } | - |
| 11376 | - | - | |
| 11377 | - | if (pred->unique_keys) | - |
| 11378 | - | appendStringInfoString(context->buf, " WITH UNIQUE KEYS"); | - |
| 11379 | - | - | |
| 11380 | - | if (!PRETTY_PAREN(context)) | - |
| 11381 | - | appendStringInfoChar(context->buf, ')'); | - |
| 11382 | - | } | - |
| 11383 | - | break; | - |
| 11384 | - | - | |
| 11385 | - | case T_JsonExpr: | - |
| 11386 | - | { | - |
| 11387 | - | JsonExpr *jexpr = (JsonExpr *) node; | - |
| 11388 | - | - | |
| 11389 | - | switch (jexpr->op) | - |
| 11390 | - | { | - |
| 11391 | - | case JSON_EXISTS_OP: | - |
| 11392 | - | appendStringInfoString(buf, "JSON_EXISTS("); | - |
| 11393 | - | break; | - |
| 11394 | - | case JSON_QUERY_OP: | - |
| 11395 | - | appendStringInfoString(buf, "JSON_QUERY("); | - |
| 11396 | - | break; | - |
| 11397 | - | case JSON_VALUE_OP: | - |
| 11398 | - | appendStringInfoString(buf, "JSON_VALUE("); | - |
| 11399 | - | break; | - |
| 11400 | - | default: | - |
| 11401 | - | elog(ERROR, "unrecognized JsonExpr op: %d", | - |
| 11402 | - | (int) jexpr->op); | - |
| 11403 | - | } | - |
| 11404 | - | - | |
| 11405 | - | get_rule_expr(jexpr->formatted_expr, context, showimplicit); | - |
| 11406 | - | - | |
| 11407 | - | appendStringInfoString(buf, ", "); | - |
| 11408 | - | - | |
| 11409 | - | get_json_path_spec(jexpr->path_spec, context, showimplicit); | - |
| 11410 | - | - | |
| 11411 | - | if (jexpr->passing_values) | - |
| 11412 | - | { | - |
| 11413 | - | ListCell *lc1, | - |
| 11414 | - | *lc2; | - |
| 11415 | - | bool needcomma = false; | - |
| 11416 | - | - | |
| 11417 | - | appendStringInfoString(buf, " PASSING "); | - |
| 11418 | - | - | |
| 11419 | - | forboth(lc1, jexpr->passing_names, | - |
| 11420 | - | lc2, jexpr->passing_values) | - |
| 11421 | - | { | - |
| 11422 | - | if (needcomma) | - |
| 11423 | - | appendStringInfoString(buf, ", "); | - |
| 11424 | - | needcomma = true; | - |
| 11425 | - | - | |
| 11426 | - | get_rule_expr((Node *) lfirst(lc2), context, showimplicit); | - |
| 11427 | - | appendStringInfo(buf, " AS %s", | - |
| 11428 | - | quote_identifier(lfirst_node(String, lc1)->sval)); | - |
| 11429 | - | } | - |
| 11430 | - | } | - |
| 11431 | - | - | |
| 11432 | - | if (jexpr->op != JSON_EXISTS_OP || | - |
| 11433 | - | jexpr->returning->typid != BOOLOID) | - |
| 11434 | - | get_json_returning(jexpr->returning, context->buf, | - |
| 11435 | - | jexpr->op == JSON_QUERY_OP); | - |
| 11436 | - | - | |
| 11437 | - | get_json_expr_options(jexpr, context, | - |
| 11438 | - | jexpr->op != JSON_EXISTS_OP ? | - |
| 11439 | - | JSON_BEHAVIOR_NULL : | - |
| 11440 | - | JSON_BEHAVIOR_FALSE); | - |
| 11441 | - | - | |
| 11442 | - | appendStringInfoChar(buf, ')'); | - |
| 11443 | - | } | - |
| 11444 | - | break; | - |
| 11445 | - | - | |
| 11446 | - | case T_List: | - |
| 11447 | - | { | - |
| 11448 | - | char *sep; | - |
| 11449 | - | ListCell *l; | - |
| 11450 | - | - | |
| 11451 | - | sep = ""; | - |
| 11452 | - | foreach(l, (List *) node) | - |
| 11453 | - | { | - |
| 11454 | - | appendStringInfoString(buf, sep); | - |
| 11455 | - | get_rule_expr((Node *) lfirst(l), context, showimplicit); | - |
| 11456 | - | sep = ", "; | - |
| 11457 | - | } | - |
| 11458 | - | } | - |
| 11459 | - | break; | - |
| 11460 | - | - | |
| 11461 | - | case T_TableFunc: | - |
| 11462 | - | get_tablefunc((TableFunc *) node, context, showimplicit); | - |
| 11463 | - | break; | - |
| 11464 | - | - | |
| 11465 | - | case T_GraphPropertyRef: | - |
| 11466 | - | { | - |
| 11467 | - | GraphPropertyRef *gpr = (GraphPropertyRef *) node; | - |
| 11468 | - | - | |
| 11469 | - | appendStringInfo(buf, "%s.%s", quote_identifier(gpr->elvarname), quote_identifier(get_propgraph_property_name(gpr->propid))); | - |
| 11470 | - | break; | - |
| 11471 | - | } | - |
| 11472 | - | - | |
| 11473 | - | default: | - |
| 11474 | - | elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); | - |
| 11475 | - | break; | - |
| 11476 | - | } | - |
| 11477 | - | } | - |
| 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 | - | } | - |
| 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 | - | } | - |
| 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 | - | } | - |
| 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 | - | } | - |
| 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 |
ReachableHow to test Use the proposed test verbatim;
it is sufficient and was empirically validated.
Add to rpr_base.sql in the rpr_navns block (after the existing navns_fn view, before DROP SCHEMA rpr_navns CASCADE at line ~1532).
A single `last` view covers all three lines via || short-circuit:
CREATE FUNCTION rpr_navns.last(integer) RETURNS integer AS 'SELECT -999' LANGUAGE sql IMMUTABLE;
CREATE VIEW navns_fn_last AS
SELECT id, count(*) OVER w AS cnt
FROM nt
WINDOW w AS (PARTITION BY g
ORDER BY id
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING INITIAL
PATTERN (A+)
DEFINE A AS rpr_navns.last(val) = -999);
SELECT pg_get_viewdef('navns_fn_last');
DROP VIEW navns_fn_last;
DROP FUNCTION rpr_navns.last(integer);
Confirmed expected viewdef output preserves the qualified call:
DEFINE
a AS (rpr_navns.last(val) = '-999'::integer) )
Capture this exact deparse in rpr_base.out.
Note the deparse also emits `AFTER MATCH SKIP PAST LAST ROW` (default) on its own line -- include it in expected output.
If a reviewer prefers explicit coverage intent, optionally also add next/first views, but they are redundant for line coverage.VerdictCONFIRMED testable and correctly diagnosed. Lines 14231-14234 are the prev/next/first/last arms of a short-circuited || chain inside the `if (inRPRDefine)` force-qualify block (ruleutils.c:14229-14235). I verified the code, the deparse mechanism, and ran the proposed test against the actual RPR build (tmp_install) in a live temp cluster. Results: (1) inRPRDefine is set true while walking DEFINE TargetEntries (get_rule_pattern_define, ruleutils.c:7237) so any FuncExpr there hits generate_function_name with inRPRDefine=true; (2) deparsing a user function `rpr_navns.last(val)` inside DEFINE yields viewdef text that preserves the schema-qualified call (`rpr_navns.last(val) = '-999'::integer`) and re-parses cleanly, proving force_qualify=true fired; (3) because `||` is left-to-right short-circuit, proname='last' evaluates strcmp(prev)==false (14231), strcmp(next)==false (14232), strcmp(first)==false (14233), strcmp(last)==true (14234) -- so the single `last` test drives execution through all three uncovered lines. I also independently confirmed `next` and `first` named user functions force qualification and round-trip. The existing navns_fn view (rpr_base.sql:1525-1529) only covers the `prev` arm (14231), matching the gcov gap exactly. No refutation found; the lines are not defensive/dead. | |||
| 14233 | 0 | strcmp(proname, "first") == 0 || | 7f135d9Recognize row pattern navigation operations by name in DEFINE |
| 14234 | 0 | strcmp(proname, "last") == 0) | 7f135d9Recognize row pattern navigation operations by name in DEFINE |
| 14235 | 4 | force_qualify = true; | 7f135d9Recognize row pattern navigation operations by name in DEFINE |
| 14236 | - | } | 7f135d9Recognize row pattern navigation operations by name in DEFINE |
| 14237 | - | 7f135d9Recognize row pattern navigation operations by name in DEFINE | |
| 14238 | - | /* | - |
| 14239 | - | * Determine whether VARIADIC should be printed. We must do this first | - |
| 14240 | - | * since it affects the lookup rules in func_get_detail(). | - |
| 14241 | - | * | - |
| 14242 | - | * We always print VARIADIC if the function has a merged variadic-array | - |
| 14243 | - | * argument. Note that this is always the case for functions taking a | - |
| 14244 | - | * VARIADIC argument type other than VARIADIC ANY. If we omitted VARIADIC | - |
| 14245 | - | * and printed the array elements as separate arguments, the call could | - |
| 14246 | - | * match a newer non-VARIADIC function. | - |
| 14247 | - | */ | - |
| 14248 | - | if (use_variadic_p) | - |
| 14249 | - | { | - |
| 14250 | - | /* Parser should not have set funcvariadic unless fn is variadic */ | - |
| 14251 | - | Assert(!has_variadic || OidIsValid(procform->provariadic)); | - |
| 14252 | - | use_variadic = has_variadic; | - |
| 14253 | - | *use_variadic_p = use_variadic; | - |
| 14254 | - | } | - |
| 14255 | - | else | - |
| 14256 | - | { | - |
| 14257 | - | Assert(!has_variadic); | - |
| 14258 | - | use_variadic = false; | - |
| 14259 | - | } | - |
| 14260 | - | - | |
| 14261 | - | /* | - |
| 14262 | - | * The idea here is to schema-qualify only if the parser would fail to | - |
| 14263 | - | * resolve the correct function given the unqualified func name with the | - |
| 14264 | - | * specified argtypes and VARIADIC flag. But if we already decided to | - |
| 14265 | - | * force qualification, then we can skip the lookup and pretend we didn't | - |
| 14266 | - | * find it. | - |
| 14267 | - | */ | - |
| 14268 | - | if (!force_qualify) | - |
| 14269 | - | p_result = func_get_detail(list_make1(makeString(proname)), | - |
| 14270 | - | NIL, argnames, nargs, argtypes, | - |
| 14271 | - | !use_variadic, true, false, | - |
| 14272 | - | &fgc_flags, | - |
| 14273 | - | &p_funcid, &p_rettype, | - |
| 14274 | - | &p_retset, &p_nvargs, &p_vatype, | - |
| 14275 | - | &p_true_typeids, NULL); | - |
| 14276 | - | else | - |
| 14277 | - | { | - |
| 14278 | - | p_result = FUNCDETAIL_NOTFOUND; | - |
| 14279 | - | p_funcid = InvalidOid; | - |
| 14280 | - | } | - |
| 14281 | - | - | |
| 14282 | - | if ((p_result == FUNCDETAIL_NORMAL || | - |
| 14283 | - | p_result == FUNCDETAIL_AGGREGATE || | - |
| 14284 | - | p_result == FUNCDETAIL_WINDOWFUNC) && | - |
| 14285 | - | p_funcid == funcid) | - |
| 14286 | - | nspname = NULL; | - |
| 14287 | - | else | - |
| 14288 | - | nspname = get_namespace_name_or_temp(procform->pronamespace); | - |
| 14289 | - | - | |
| 14290 | - | result = quote_qualified_identifier(nspname, proname); | - |
| 14291 | - | - | |
| 14292 | - | ReleaseSysCache(proctup); | - |
| 14293 | - | - | |
| 14294 | - | return result; | - |
| 14295 | - | } | - |