1 /*-------------------------------------------------------------------------
2 *
3 * deparse.c
4 * Query deparser for mysql_fdw
5 *
6 * Portions Copyright (c) 2012-2014, PostgreSQL Global Development Group
7 * Portions Copyright (c) 2004-2021, EnterpriseDB Corporation.
8 *
9 * IDENTIFICATION
10 * deparse.c
11 *
12 *-------------------------------------------------------------------------
13 */
14 #include "postgres.h"
15
16 #include "access/heapam.h"
17 #include "access/htup_details.h"
18 #include "access/sysattr.h"
19 #include "access/transam.h"
20 #include "catalog/pg_collation.h"
21 #include "catalog/pg_namespace.h"
22 #include "catalog/pg_operator.h"
23 #include "catalog/pg_proc.h"
24 #include "catalog/pg_type.h"
25 #include "commands/defrem.h"
26 #include "datatype/timestamp.h"
27 #include "mysql_fdw.h"
28 #include "nodes/nodeFuncs.h"
29 #include "nodes/plannodes.h"
30 #include "optimizer/clauses.h"
31 #if PG_VERSION_NUM < 120000
32 #include "optimizer/var.h"
33 #else
34 #include "optimizer/optimizer.h"
35 #endif
36 #include "optimizer/prep.h"
37 #include "optimizer/tlist.h"
38 #include "parser/parsetree.h"
39 #include "pgtime.h"
40 #include "utils/builtins.h"
41 #include "utils/lsyscache.h"
42 #include "utils/syscache.h"
43 #include "utils/timestamp.h"
44
45
46 static char *mysql_quote_identifier(const char *str, char quotechar);
47
48 /*
49 * Global context for foreign_expr_walker's search of an expression tree.
50 */
51 typedef struct foreign_glob_cxt
52 {
53 PlannerInfo *root; /* global planner state */
54 RelOptInfo *foreignrel; /* the foreign relation we are planning for */
55
56 /*
57 * For join pushdown, only a limited set of operators are allowed to be
58 * pushed. This flag helps us identify if we are walking through the list
59 * of join conditions.
60 */
61 bool is_join_cond; /* true for join relations */
62 } foreign_glob_cxt;
63
64 /*
65 * Local (per-tree-level) context for foreign_expr_walker's search.
66 * This is concerned with identifying collations used in the expression.
67 */
68 typedef enum
69 {
70 FDW_COLLATE_NONE, /* expression is of a noncollatable type */
71 FDW_COLLATE_SAFE, /* collation derives from a foreign Var */
72 FDW_COLLATE_UNSAFE /* collation derives from something else */
73 } FDWCollateState;
74
75 typedef struct foreign_loc_cxt
76 {
77 Oid collation; /* OID of current collation, if any */
78 FDWCollateState state; /* state of current collation choice */
79 } foreign_loc_cxt;
80
81 /*
82 * Context for deparseExpr
83 */
84 typedef struct deparse_expr_cxt
85 {
86 PlannerInfo *root; /* global planner state */
87 RelOptInfo *foreignrel; /* the foreign relation we are planning for */
88 StringInfo buf; /* output buffer to append to */
89 List **params_list; /* exprs that will become remote Params */
90 } deparse_expr_cxt;
91
92 #define REL_ALIAS_PREFIX "r"
93 /* Handy macro to add relation name qualification */
94 #define ADD_REL_QUALIFIER(buf, varno) \
95 appendStringInfo((buf), "%s%d.", REL_ALIAS_PREFIX, (varno))
96
97 /*
98 * Functions to construct string representation of a node tree.
99 */
100 static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
101 static void mysql_deparse_var(Var *node, deparse_expr_cxt *context);
102 static void mysql_deparse_const(Const *node, deparse_expr_cxt *context);
103 static void mysql_deparse_param(Param *node, deparse_expr_cxt *context);
104 #if PG_VERSION_NUM < 120000
105 static void mysql_deparse_array_ref(ArrayRef *node, deparse_expr_cxt *context);
106 #else
107 static void mysql_deparse_array_ref(SubscriptingRef *node,
108 deparse_expr_cxt *context);
109 #endif
110 static void mysql_deparse_func_expr(FuncExpr *node, deparse_expr_cxt *context);
111 static void mysql_deparse_op_expr(OpExpr *node, deparse_expr_cxt *context);
112 static void mysql_deparse_operator_name(StringInfo buf,
113 Form_pg_operator opform);
114 static void mysql_deparse_distinct_expr(DistinctExpr *node,
115 deparse_expr_cxt *context);
116 static void mysql_deparse_scalar_array_op_expr(ScalarArrayOpExpr *node,
117 deparse_expr_cxt *context);
118 static void mysql_deparse_relabel_type(RelabelType *node,
119 deparse_expr_cxt *context);
120 static void mysql_deparse_bool_expr(BoolExpr *node, deparse_expr_cxt *context);
121 static void mysql_deparse_null_test(NullTest *node, deparse_expr_cxt *context);
122 static void mysql_deparse_array_expr(ArrayExpr *node,
123 deparse_expr_cxt *context);
124 static void mysql_print_remote_param(int paramindex, Oid paramtype,
125 int32 paramtypmod,
126 deparse_expr_cxt *context);
127 static void mysql_print_remote_placeholder(Oid paramtype, int32 paramtypmod,
128 deparse_expr_cxt *context);
129 static void mysql_deparse_relation(StringInfo buf, Relation rel);
130 static void mysql_deparse_target_list(StringInfo buf, PlannerInfo *root,
131 Index rtindex, Relation rel,
132 Bitmapset *attrs_used,
133 List **retrieved_attrs);
134 static void mysql_deparse_column_ref(StringInfo buf, int varno, int varattno,
135 PlannerInfo *root, bool qualify_col);
136 static void mysql_deparse_select_sql(List *tlist, List **retrieved_attrs,
137 deparse_expr_cxt *context);
138 static void mysql_append_conditions(List *exprs, deparse_expr_cxt *context);
139 static void mysql_deparse_explicit_target_list(List *tlist,
140 List **retrieved_attrs,
141 deparse_expr_cxt *context);
142 static void mysql_deparse_from_expr(StringInfo buf, PlannerInfo *root,
143 RelOptInfo *foreignrel, bool use_alias,
144 List **param_list);
145
146 /*
147 * Functions to construct string representation of a specific types.
148 */
149 static void deparse_interval(StringInfo buf, Datum datum);
150
151 /*
152 * Local variables.
153 */
154 static char *cur_opname = NULL;
155
156 /*
157 * Append remote name of specified foreign table to buf. Use value of
158 * table_name FDW option (if any) instead of relation's name. Similarly,
159 * schema_name FDW option overrides schema name.
160 */
161 static void
mysql_deparse_relation(StringInfo buf,Relation rel)162 mysql_deparse_relation(StringInfo buf, Relation rel)
163 {
164 ForeignTable *table;
165 const char *nspname = NULL;
166 const char *relname = NULL;
167 ListCell *lc;
168
169 /* Obtain additional catalog information. */
170 table = GetForeignTable(RelationGetRelid(rel));
171
172 /*
173 * Use value of FDW options if any, instead of the name of object itself.
174 */
175 foreach(lc, table->options)
176 {
177 DefElem *def = (DefElem *) lfirst(lc);
178
179 if (strcmp(def->defname, "dbname") == 0)
180 nspname = defGetString(def);
181 else if (strcmp(def->defname, "table_name") == 0)
182 relname = defGetString(def);
183 }
184
185 /*
186 * Note: we could skip printing the schema name if it's pg_catalog, but
187 * that doesn't seem worth the trouble.
188 */
189 if (nspname == NULL)
190 nspname = get_namespace_name(RelationGetNamespace(rel));
191 if (relname == NULL)
192 relname = RelationGetRelationName(rel);
193
194 appendStringInfo(buf, "%s.%s", mysql_quote_identifier(nspname, '`'),
195 mysql_quote_identifier(relname, '`'));
196 }
197
198 static char *
mysql_quote_identifier(const char * str,char quotechar)199 mysql_quote_identifier(const char *str, char quotechar)
200 {
201 char *result = palloc(strlen(str) * 2 + 3);
202 char *res = result;
203
204 *res++ = quotechar;
205 while (*str)
206 {
207 if (*str == quotechar)
208 *res++ = *str;
209 *res++ = *str;
210 str++;
211 }
212 *res++ = quotechar;
213 *res++ = '\0';
214
215 return result;
216 }
217
218 /*
219 * mysql_deparse_select_stmt_for_rel
220 * Deparse SELECT statement for given relation into buf.
221 *
222 * tlist contains the list of desired columns to be fetched from foreign
223 * server. For a base relation fpinfo->attrs_used is used to construct
224 * SELECT clause, hence the tlist is ignored for a base relation.
225 *
226 * remote_conds is the list of conditions to be deparsed into the WHERE clause.
227 *
228 * If params_list is not NULL, it receives a list of Params and other-relation
229 * Vars used in the clauses; these values must be transmitted to the remote
230 * server as parameter values.
231 *
232 * If params_list is NULL, we're generating the query for EXPLAIN purposes,
233 * so Params and other-relation Vars should be replaced by dummy values.
234 *
235 * List of columns selected is returned in retrieved_attrs.
236 */
237 extern void
mysql_deparse_select_stmt_for_rel(StringInfo buf,PlannerInfo * root,RelOptInfo * rel,List * tlist,List * remote_conds,List ** retrieved_attrs,List ** params_list)238 mysql_deparse_select_stmt_for_rel(StringInfo buf, PlannerInfo *root,
239 RelOptInfo *rel, List *tlist,
240 List *remote_conds, List **retrieved_attrs,
241 List **params_list)
242 {
243 deparse_expr_cxt context;
244
245 /* We handle relations for foreign tables and joins between those */
246 #if PG_VERSION_NUM >= 100000
247 Assert(IS_JOIN_REL(rel) || IS_SIMPLE_REL(rel));
248 #else
249 Assert(rel->reloptkind == RELOPT_JOINREL ||
250 rel->reloptkind == RELOPT_BASEREL ||
251 rel->reloptkind == RELOPT_OTHER_MEMBER_REL);
252 #endif
253
254 /* Fill portions of context common to base relation */
255 context.buf = buf;
256 context.root = root;
257 context.foreignrel = rel;
258 context.params_list = params_list;
259
260 /* Construct SELECT clause and FROM clause */
261 mysql_deparse_select_sql(tlist, retrieved_attrs, &context);
262
263 /* Construct WHERE clause */
264 if (remote_conds != NIL)
265 {
266 appendStringInfoString(buf, " WHERE ");
267 mysql_append_conditions(remote_conds, &context);
268 }
269 }
270
271 /*
272 * mysql_deparse_select_sql
273 * Construct a simple SELECT statement that retrieves desired columns
274 * of the specified foreign table, and append it to "buf". The output
275 * contains just "SELECT ... FROM ....".
276 *
277 * tlist is the list of desired columns. Read prologue of
278 * mysql_deparse_select_stmt_for_rel() for details.
279 *
280 * We also create an integer List of the columns being retrieved, which is
281 * returned to *retrieved_attrs.
282 */
283 static void
mysql_deparse_select_sql(List * tlist,List ** retrieved_attrs,deparse_expr_cxt * context)284 mysql_deparse_select_sql(List *tlist, List **retrieved_attrs,
285 deparse_expr_cxt *context)
286 {
287 StringInfo buf = context->buf;
288 RelOptInfo *foreignrel = context->foreignrel;
289 PlannerInfo *root = context->root;
290
291 /*
292 * Construct SELECT list
293 */
294 appendStringInfoString(buf, "SELECT ");
295
296 #if PG_VERSION_NUM >= 100000
297 if (IS_JOIN_REL(foreignrel))
298 #else
299 if (foreignrel->reloptkind == RELOPT_JOINREL)
300 #endif
301 {
302 /* For a join relation use the input tlist */
303 mysql_deparse_explicit_target_list(tlist, retrieved_attrs, context);
304
305 /*
306 * Construct FROM clause
307 */
308 appendStringInfoString(buf, " FROM ");
309 mysql_deparse_from_expr(buf, root, foreignrel, true, context->params_list);
310 }
311 else
312 {
313 RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
314 Relation rel;
315 MySQLFdwRelationInfo *fpinfo = (MySQLFdwRelationInfo *) foreignrel->fdw_private;
316
317 /*
318 * Core code already has some lock on each rel being planned, so we can
319 * use NoLock here.
320 */
321 #if PG_VERSION_NUM < 130000
322 rel = heap_open(rte->relid, NoLock);
323 #else
324 rel = table_open(rte->relid, NoLock);
325 #endif
326
327 mysql_deparse_target_list(buf, root, foreignrel->relid, rel,
328 fpinfo->attrs_used, retrieved_attrs);
329
330 /*
331 * Construct FROM clause
332 */
333 appendStringInfoString(buf, " FROM ");
334 mysql_deparse_relation(buf, rel);
335
336 #if PG_VERSION_NUM < 130000
337 heap_close(rel, NoLock);
338 #else
339 table_close(rel, NoLock);
340 #endif
341 }
342 }
343
344 /*
345 * mysql_deparse_explicit_target_list
346 * Deparse given targetlist and append it to context->buf.
347 *
348 * retrieved_attrs is the list of continuously increasing integers starting
349 * from 1. It has same number of entries as tlist.
350 */
351 static void
mysql_deparse_explicit_target_list(List * tlist,List ** retrieved_attrs,deparse_expr_cxt * context)352 mysql_deparse_explicit_target_list(List *tlist, List **retrieved_attrs,
353 deparse_expr_cxt *context)
354 {
355 ListCell *lc;
356 StringInfo buf = context->buf;
357 int i = 0;
358
359 *retrieved_attrs = NIL;
360
361 foreach(lc, tlist)
362 {
363 Var *var;
364
365 var = (Var *) lfirst(lc);
366 /* We expect only Var nodes here */
367 Assert(IsA(var, Var));
368
369 if (i > 0)
370 appendStringInfoString(buf, ", ");
371 mysql_deparse_var(var, context);
372
373 *retrieved_attrs = lappend_int(*retrieved_attrs, i + 1);
374
375 i++;
376 }
377
378 if (i == 0)
379 appendStringInfoString(buf, "NULL");
380 }
381
382 /*
383 * Deparse remote INSERT statement
384 *
385 * The statement text is appended to buf, and we also create an integer List
386 * of the columns being retrieved by RETURNING (if any), which is returned
387 * to *retrieved_attrs.
388 */
389 void
mysql_deparse_insert(StringInfo buf,PlannerInfo * root,Index rtindex,Relation rel,List * targetAttrs)390 mysql_deparse_insert(StringInfo buf, PlannerInfo *root, Index rtindex,
391 Relation rel, List *targetAttrs)
392 {
393 ListCell *lc;
394
395 appendStringInfoString(buf, "INSERT INTO ");
396 mysql_deparse_relation(buf, rel);
397
398 if (targetAttrs)
399 {
400 AttrNumber pindex;
401 bool first;
402
403 appendStringInfoChar(buf, '(');
404
405 first = true;
406 foreach(lc, targetAttrs)
407 {
408 int attnum = lfirst_int(lc);
409
410 if (!first)
411 appendStringInfoString(buf, ", ");
412 first = false;
413
414 mysql_deparse_column_ref(buf, rtindex, attnum, root, false);
415 }
416
417 appendStringInfoString(buf, ") VALUES (");
418
419 pindex = 1;
420 first = true;
421 foreach(lc, targetAttrs)
422 {
423 if (!first)
424 appendStringInfoString(buf, ", ");
425 first = false;
426
427 appendStringInfo(buf, "?");
428 pindex++;
429 }
430
431 appendStringInfoChar(buf, ')');
432 }
433 else
434 appendStringInfoString(buf, " DEFAULT VALUES");
435 }
436
437 void
mysql_deparse_analyze(StringInfo sql,char * dbname,char * relname)438 mysql_deparse_analyze(StringInfo sql, char *dbname, char *relname)
439 {
440 appendStringInfo(sql, "SELECT");
441 appendStringInfo(sql, " round(((data_length + index_length)), 2)");
442 appendStringInfo(sql, " FROM information_schema.TABLES");
443 appendStringInfo(sql, " WHERE table_schema = '%s' AND table_name = '%s'",
444 dbname, relname);
445 }
446
447 /*
448 * Emit a target list that retrieves the columns specified in attrs_used.
449 * This is used for both SELECT and RETURNING targetlists.
450 */
451 static void
mysql_deparse_target_list(StringInfo buf,PlannerInfo * root,Index rtindex,Relation rel,Bitmapset * attrs_used,List ** retrieved_attrs)452 mysql_deparse_target_list(StringInfo buf, PlannerInfo *root, Index rtindex,
453 Relation rel, Bitmapset *attrs_used,
454 List **retrieved_attrs)
455 {
456 TupleDesc tupdesc = RelationGetDescr(rel);
457 bool have_wholerow;
458 bool first;
459 int i;
460
461 /* If there's a whole-row reference, we'll need all the columns. */
462 have_wholerow = bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
463 attrs_used);
464
465 first = true;
466
467 *retrieved_attrs = NIL;
468 for (i = 1; i <= tupdesc->natts; i++)
469 {
470 Form_pg_attribute attr = TupleDescAttr(tupdesc, i - 1);
471
472 /* Ignore dropped attributes. */
473 if (attr->attisdropped)
474 continue;
475
476 if (have_wholerow ||
477 bms_is_member(i - FirstLowInvalidHeapAttributeNumber, attrs_used))
478 {
479 if (!first)
480 appendStringInfoString(buf, ", ");
481 first = false;
482
483 mysql_deparse_column_ref(buf, rtindex, i, root, false);
484 *retrieved_attrs = lappend_int(*retrieved_attrs, i);
485 }
486 }
487
488 /* Don't generate bad syntax if no undropped columns */
489 if (first)
490 appendStringInfoString(buf, "NULL");
491 }
492
493 /*
494 * Construct name to use for given column, and emit it into buf. If it has a
495 * column_name FDW option, use that instead of attribute name.
496 */
497 static void
mysql_deparse_column_ref(StringInfo buf,int varno,int varattno,PlannerInfo * root,bool qualify_col)498 mysql_deparse_column_ref(StringInfo buf, int varno, int varattno,
499 PlannerInfo *root, bool qualify_col)
500 {
501 RangeTblEntry *rte;
502 char *colname = NULL;
503 List *options;
504 ListCell *lc;
505
506 /* varno must not be any of OUTER_VAR, INNER_VAR and INDEX_VAR. */
507 Assert(!IS_SPECIAL_VARNO(varno));
508
509 /* Get RangeTblEntry from array in PlannerInfo. */
510 rte = planner_rt_fetch(varno, root);
511
512 /*
513 * If it's a column of a foreign table, and it has the column_name FDW
514 * option, use that value.
515 */
516 options = GetForeignColumnOptions(rte->relid, varattno);
517 foreach(lc, options)
518 {
519 DefElem *def = (DefElem *) lfirst(lc);
520
521 if (strcmp(def->defname, "column_name") == 0)
522 {
523 colname = defGetString(def);
524 break;
525 }
526 }
527
528 /*
529 * If it's a column of a regular table or it doesn't have column_name
530 * FDW option, use attribute name.
531 */
532 if (colname == NULL)
533 #if PG_VERSION_NUM >= 110000
534 colname = get_attname(rte->relid, varattno, false);
535 #else
536 colname = get_relid_attribute_name(rte->relid, varattno);
537 #endif
538
539 if (qualify_col)
540 ADD_REL_QUALIFIER(buf, varno);
541
542 appendStringInfoString(buf, mysql_quote_identifier(colname, '`'));
543 }
544
545 static void
mysql_deparse_string(StringInfo buf,const char * val,bool isstr)546 mysql_deparse_string(StringInfo buf, const char *val, bool isstr)
547 {
548 const char *valptr;
549 int i = 0;
550
551 if (isstr)
552 appendStringInfoChar(buf, '\'');
553
554 for (valptr = val; *valptr; valptr++,i++)
555 {
556 char ch = *valptr;
557
558 /*
559 * Remove '{', '}', and \" character from the string. Because this
560 * syntax is not recognize by the remote MySQL server.
561 */
562 if ((ch == '{' && i == 0) || (ch == '}' && (i == (strlen(val) - 1))) ||
563 ch == '\"')
564 continue;
565
566 if (isstr && ch == ',')
567 {
568 appendStringInfoString(buf, "', '");
569 continue;
570 }
571 appendStringInfoChar(buf, ch);
572 }
573
574 if (isstr)
575 appendStringInfoChar(buf, '\'');
576 }
577
578 /*
579 * Append a SQL string literal representing "val" to buf.
580 */
581 static void
mysql_deparse_string_literal(StringInfo buf,const char * val)582 mysql_deparse_string_literal(StringInfo buf, const char *val)
583 {
584 const char *valptr;
585
586 appendStringInfoChar(buf, '\'');
587
588 for (valptr = val; *valptr; valptr++)
589 {
590 char ch = *valptr;
591
592 if (SQL_STR_DOUBLE(ch, true))
593 appendStringInfoChar(buf, ch);
594 appendStringInfoChar(buf, ch);
595 }
596
597 appendStringInfoChar(buf, '\'');
598 }
599
600 /*
601 * Deparse given expression into context->buf.
602 *
603 * This function must support all the same node types that foreign_expr_walker
604 * accepts.
605 *
606 * Note: unlike ruleutils.c, we just use a simple hard-wired parenthesization
607 * scheme: anything more complex than a Var, Const, function call or cast
608 * should be self-parenthesized.
609 */
610 static void
deparseExpr(Expr * node,deparse_expr_cxt * context)611 deparseExpr(Expr *node, deparse_expr_cxt *context)
612 {
613 if (node == NULL)
614 return;
615
616 switch (nodeTag(node))
617 {
618 case T_Var:
619 mysql_deparse_var((Var *) node, context);
620 break;
621 case T_Const:
622 mysql_deparse_const((Const *) node, context);
623 break;
624 case T_Param:
625 mysql_deparse_param((Param *) node, context);
626 break;
627 #if PG_VERSION_NUM < 120000
628 case T_ArrayRef:
629 mysql_deparse_array_ref((ArrayRef *) node, context);
630 #else
631 case T_SubscriptingRef:
632 mysql_deparse_array_ref((SubscriptingRef *) node, context);
633 #endif
634 break;
635 case T_FuncExpr:
636 mysql_deparse_func_expr((FuncExpr *) node, context);
637 break;
638 case T_OpExpr:
639 mysql_deparse_op_expr((OpExpr *) node, context);
640 break;
641 case T_DistinctExpr:
642 mysql_deparse_distinct_expr((DistinctExpr *) node, context);
643 break;
644 case T_ScalarArrayOpExpr:
645 mysql_deparse_scalar_array_op_expr((ScalarArrayOpExpr *) node,
646 context);
647 break;
648 case T_RelabelType:
649 mysql_deparse_relabel_type((RelabelType *) node, context);
650 break;
651 case T_BoolExpr:
652 mysql_deparse_bool_expr((BoolExpr *) node, context);
653 break;
654 case T_NullTest:
655 mysql_deparse_null_test((NullTest *) node, context);
656 break;
657 case T_ArrayExpr:
658 mysql_deparse_array_expr((ArrayExpr *) node, context);
659 break;
660 default:
661 elog(ERROR, "unsupported expression type for deparse: %d",
662 (int) nodeTag(node));
663 break;
664 }
665 }
666
667 /*
668 * Deparse Interval type into MySQL Interval representation.
669 */
670 static void
deparse_interval(StringInfo buf,Datum datum)671 deparse_interval(StringInfo buf, Datum datum)
672 {
673 struct pg_tm tm;
674 fsec_t fsec;
675 bool is_first = true;
676
677 #define append_interval(expr, unit) \
678 do { \
679 if (!is_first) \
680 appendStringInfo(buf, " %s ", cur_opname); \
681 appendStringInfo(buf, "INTERVAL %d %s", expr, unit); \
682 is_first = false; \
683 } while (0)
684
685 /* Check saved opname. It could be only "+" and "-" */
686 Assert(cur_opname);
687
688 if (interval2tm(*DatumGetIntervalP(datum), &tm, &fsec) != 0)
689 elog(ERROR, "could not convert interval to tm");
690
691 if (tm.tm_year > 0)
692 append_interval(tm.tm_year, "YEAR");
693
694 if (tm.tm_mon > 0)
695 append_interval(tm.tm_mon, "MONTH");
696
697 if (tm.tm_mday > 0)
698 append_interval(tm.tm_mday, "DAY");
699
700 if (tm.tm_hour > 0)
701 append_interval(tm.tm_hour, "HOUR");
702
703 if (tm.tm_min > 0)
704 append_interval(tm.tm_min, "MINUTE");
705
706 if (tm.tm_sec > 0)
707 append_interval(tm.tm_sec, "SECOND");
708
709 if (fsec > 0)
710 {
711 if (!is_first)
712 appendStringInfo(buf, " %s ", cur_opname);
713 #ifdef HAVE_INT64_TIMESTAMP
714 appendStringInfo(buf, "INTERVAL %d MICROSECOND", fsec);
715 #else
716 appendStringInfo(buf, "INTERVAL %f MICROSECOND", fsec);
717 #endif
718 }
719 }
720
721 /*
722 * Deparse remote UPDATE statement
723 *
724 * The statement text is appended to buf, and we also create an integer List
725 * of the columns being retrieved by RETURNING (if any), which is returned
726 * to *retrieved_attrs.
727 */
728 void
mysql_deparse_update(StringInfo buf,PlannerInfo * root,Index rtindex,Relation rel,List * targetAttrs,char * attname)729 mysql_deparse_update(StringInfo buf, PlannerInfo *root, Index rtindex,
730 Relation rel, List *targetAttrs, char *attname)
731 {
732 AttrNumber pindex;
733 bool first;
734 ListCell *lc;
735
736 appendStringInfoString(buf, "UPDATE ");
737 mysql_deparse_relation(buf, rel);
738 appendStringInfoString(buf, " SET ");
739
740 pindex = 2;
741 first = true;
742 foreach(lc, targetAttrs)
743 {
744 int attnum = lfirst_int(lc);
745
746 if (attnum == 1)
747 continue;
748
749 if (!first)
750 appendStringInfoString(buf, ", ");
751 first = false;
752
753 mysql_deparse_column_ref(buf, rtindex, attnum, root, false);
754 appendStringInfo(buf, " = ?");
755 pindex++;
756 }
757
758 appendStringInfo(buf, " WHERE %s = ?", attname);
759 }
760
761 /*
762 * Deparse remote DELETE statement
763 *
764 * The statement text is appended to buf, and we also create an integer List
765 * of the columns being retrieved by RETURNING (if any), which is returned
766 * to *retrieved_attrs.
767 */
768 void
mysql_deparse_delete(StringInfo buf,PlannerInfo * root,Index rtindex,Relation rel,char * name)769 mysql_deparse_delete(StringInfo buf, PlannerInfo *root, Index rtindex,
770 Relation rel, char *name)
771 {
772 appendStringInfoString(buf, "DELETE FROM ");
773 mysql_deparse_relation(buf, rel);
774 appendStringInfo(buf, " WHERE %s = ?", name);
775 }
776
777 /*
778 * Deparse given Var node into context->buf.
779 *
780 * If the Var belongs to the foreign relation, just print its remote name.
781 * Otherwise, it's effectively a Param (and will in fact be a Param at
782 * run time). Handle it the same way we handle plain Params --- see
783 * deparseParam for comments.
784 */
785 static void
mysql_deparse_var(Var * node,deparse_expr_cxt * context)786 mysql_deparse_var(Var *node, deparse_expr_cxt *context)
787 {
788 Relids relids = context->foreignrel->relids;
789 bool qualify_col = (bms_membership(relids) == BMS_MULTIPLE);
790
791 if (bms_is_member(node->varno, relids) && node->varlevelsup == 0)
792 {
793 /* Var belongs to foreign table */
794 mysql_deparse_column_ref(context->buf, node->varno, node->varattno,
795 context->root, qualify_col);
796 }
797 else
798 {
799 /* Treat like a Param */
800 if (context->params_list)
801 {
802 int pindex = 0;
803 ListCell *lc;
804
805 /* Find its index in params_list */
806 foreach(lc, *context->params_list)
807 {
808 pindex++;
809 if (equal(node, (Node *) lfirst(lc)))
810 break;
811 }
812 if (lc == NULL)
813 {
814 /* Not in list, so add it */
815 pindex++;
816 *context->params_list = lappend(*context->params_list, node);
817 }
818 mysql_print_remote_param(pindex, node->vartype, node->vartypmod,
819 context);
820 }
821 else
822 mysql_print_remote_placeholder(node->vartype, node->vartypmod,
823 context);
824 }
825 }
826
827 /*
828 * Deparse given constant value into context->buf.
829 *
830 * This function has to be kept in sync with ruleutils.c's get_const_expr.
831 */
832 static void
mysql_deparse_const(Const * node,deparse_expr_cxt * context)833 mysql_deparse_const(Const *node, deparse_expr_cxt *context)
834 {
835 StringInfo buf = context->buf;
836 Oid typoutput;
837 bool typIsVarlena;
838 char *extval;
839
840 if (node->constisnull)
841 {
842 appendStringInfoString(buf, "NULL");
843 return;
844 }
845
846 getTypeOutputInfo(node->consttype, &typoutput, &typIsVarlena);
847
848 switch (node->consttype)
849 {
850 case INT2OID:
851 case INT4OID:
852 case INT8OID:
853 case OIDOID:
854 case FLOAT4OID:
855 case FLOAT8OID:
856 case NUMERICOID:
857 {
858 extval = OidOutputFunctionCall(typoutput, node->constvalue);
859
860 /*
861 * No need to quote unless it's a special value such as 'NaN'.
862 * See comments in get_const_expr().
863 */
864 if (strspn(extval, "0123456789+-eE.") == strlen(extval))
865 {
866 if (extval[0] == '+' || extval[0] == '-')
867 appendStringInfo(buf, "(%s)", extval);
868 else
869 appendStringInfoString(buf, extval);
870 }
871 else
872 appendStringInfo(buf, "'%s'", extval);
873 }
874 break;
875 case BITOID:
876 case VARBITOID:
877 extval = OidOutputFunctionCall(typoutput, node->constvalue);
878 appendStringInfo(buf, "B'%s'", extval);
879 break;
880 case BOOLOID:
881 extval = OidOutputFunctionCall(typoutput, node->constvalue);
882 if (strcmp(extval, "t") == 0)
883 appendStringInfoString(buf, "true");
884 else
885 appendStringInfoString(buf, "false");
886 break;
887 case INTERVALOID:
888 deparse_interval(buf, node->constvalue);
889 break;
890 case BYTEAOID:
891 /*
892 * The string for BYTEA always seems to be in the format "\\x##"
893 * where # is a hex digit, Even if the value passed in is
894 * 'hi'::bytea we will receive "\x6869". Making this assumption
895 * allows us to quickly convert postgres escaped strings to mysql
896 * ones for comparison
897 */
898 extval = OidOutputFunctionCall(typoutput, node->constvalue);
899 appendStringInfo(buf, "X\'%s\'", extval + 2);
900 break;
901 default:
902 extval = OidOutputFunctionCall(typoutput, node->constvalue);
903 mysql_deparse_string_literal(buf, extval);
904 break;
905 }
906 }
907
908 /*
909 * Deparse given Param node.
910 *
911 * If we're generating the query "for real", add the Param to
912 * context->params_list if it's not already present, and then use its index
913 * in that list as the remote parameter number. During EXPLAIN, there's
914 * no need to identify a parameter number.
915 */
916 static void
mysql_deparse_param(Param * node,deparse_expr_cxt * context)917 mysql_deparse_param(Param *node, deparse_expr_cxt *context)
918 {
919 if (context->params_list)
920 {
921 int pindex = 0;
922 ListCell *lc;
923
924 /* Find its index in params_list */
925 foreach(lc, *context->params_list)
926 {
927 pindex++;
928 if (equal(node, (Node *) lfirst(lc)))
929 break;
930 }
931 if (lc == NULL)
932 {
933 /* Not in list, so add it */
934 pindex++;
935 *context->params_list = lappend(*context->params_list, node);
936 }
937
938 mysql_print_remote_param(pindex, node->paramtype, node->paramtypmod,
939 context);
940 }
941 else
942 mysql_print_remote_placeholder(node->paramtype, node->paramtypmod,
943 context);
944 }
945
946 /*
947 * Deparse an array subscript expression.
948 */
949 static void
950 #if PG_VERSION_NUM < 120000
mysql_deparse_array_ref(ArrayRef * node,deparse_expr_cxt * context)951 mysql_deparse_array_ref(ArrayRef *node, deparse_expr_cxt *context)
952 #else
953 mysql_deparse_array_ref(SubscriptingRef *node, deparse_expr_cxt *context)
954 #endif
955 {
956 StringInfo buf = context->buf;
957 ListCell *lowlist_item;
958 ListCell *uplist_item;
959
960 /* Always parenthesize the expression. */
961 appendStringInfoChar(buf, '(');
962
963 /*
964 * Deparse referenced array expression first. If that expression includes
965 * a cast, we have to parenthesize to prevent the array subscript from
966 * being taken as typename decoration. We can avoid that in the typical
967 * case of subscripting a Var, but otherwise do it.
968 */
969 if (IsA(node->refexpr, Var))
970 deparseExpr(node->refexpr, context);
971 else
972 {
973 appendStringInfoChar(buf, '(');
974 deparseExpr(node->refexpr, context);
975 appendStringInfoChar(buf, ')');
976 }
977
978 /* Deparse subscript expressions. */
979 lowlist_item = list_head(node->reflowerindexpr); /* could be NULL */
980 foreach(uplist_item, node->refupperindexpr)
981 {
982 appendStringInfoChar(buf, '[');
983 if (lowlist_item)
984 {
985 deparseExpr(lfirst(lowlist_item), context);
986 appendStringInfoChar(buf, ':');
987 #if PG_VERSION_NUM < 130000
988 lowlist_item = lnext(lowlist_item);
989 #else
990 lowlist_item = lnext(node->reflowerindexpr, lowlist_item);
991 #endif
992 }
993 deparseExpr(lfirst(uplist_item), context);
994 appendStringInfoChar(buf, ']');
995 }
996
997 appendStringInfoChar(buf, ')');
998 }
999
1000 /*
1001 * This is possible that the name of function in PostgreSQL and mysql differ,
1002 * so return the mysql eloquent function name.
1003 */
1004 static char *
mysql_replace_function(char * in)1005 mysql_replace_function(char *in)
1006 {
1007 if (strcmp(in, "btrim") == 0)
1008 return "trim";
1009
1010 return in;
1011 }
1012
1013 /*
1014 * Deparse a function call.
1015 */
1016 static void
mysql_deparse_func_expr(FuncExpr * node,deparse_expr_cxt * context)1017 mysql_deparse_func_expr(FuncExpr *node, deparse_expr_cxt *context)
1018 {
1019 StringInfo buf = context->buf;
1020 HeapTuple proctup;
1021 Form_pg_proc procform;
1022 const char *proname;
1023 bool first;
1024 ListCell *arg;
1025
1026 /*
1027 * Normal function: display as proname(args).
1028 */
1029 proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(node->funcid));
1030 if (!HeapTupleIsValid(proctup))
1031 elog(ERROR, "cache lookup failed for function %u", node->funcid);
1032
1033 procform = (Form_pg_proc) GETSTRUCT(proctup);
1034
1035 /* Translate PostgreSQL function into mysql function */
1036 proname = mysql_replace_function(NameStr(procform->proname));
1037
1038 /* Deparse the function name ... */
1039 appendStringInfo(buf, "%s(", proname);
1040
1041 /* ... and all the arguments */
1042 first = true;
1043 foreach(arg, node->args)
1044 {
1045 if (!first)
1046 appendStringInfoString(buf, ", ");
1047 deparseExpr((Expr *) lfirst(arg), context);
1048 first = false;
1049 }
1050
1051 appendStringInfoChar(buf, ')');
1052
1053 ReleaseSysCache(proctup);
1054 }
1055
1056 /*
1057 * Deparse given operator expression. To avoid problems around
1058 * priority of operations, we always parenthesize the arguments.
1059 */
1060 static void
mysql_deparse_op_expr(OpExpr * node,deparse_expr_cxt * context)1061 mysql_deparse_op_expr(OpExpr *node, deparse_expr_cxt *context)
1062 {
1063 StringInfo buf = context->buf;
1064 HeapTuple tuple;
1065 Form_pg_operator form;
1066 char oprkind;
1067 ListCell *arg;
1068
1069 /* Retrieve information about the operator from system catalog. */
1070 tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
1071 if (!HeapTupleIsValid(tuple))
1072 elog(ERROR, "cache lookup failed for operator %u", node->opno);
1073
1074 form = (Form_pg_operator) GETSTRUCT(tuple);
1075 oprkind = form->oprkind;
1076
1077 /* Sanity check. */
1078 Assert((oprkind == 'r' && list_length(node->args) == 1) ||
1079 (oprkind == 'l' && list_length(node->args) == 1) ||
1080 (oprkind == 'b' && list_length(node->args) == 2));
1081
1082 /* Always parenthesize the expression. */
1083 appendStringInfoChar(buf, '(');
1084
1085 /* Deparse left operand. */
1086 if (oprkind == 'r' || oprkind == 'b')
1087 {
1088 arg = list_head(node->args);
1089 deparseExpr(lfirst(arg), context);
1090 appendStringInfoChar(buf, ' ');
1091 }
1092
1093 /* Deparse operator name. */
1094 mysql_deparse_operator_name(buf, form);
1095
1096 /* Deparse right operand. */
1097 if (oprkind == 'l' || oprkind == 'b')
1098 {
1099 arg = list_tail(node->args);
1100 appendStringInfoChar(buf, ' ');
1101 deparseExpr(lfirst(arg), context);
1102 }
1103
1104 appendStringInfoChar(buf, ')');
1105
1106 ReleaseSysCache(tuple);
1107 }
1108
1109 /*
1110 * Print the name of an operator.
1111 */
1112 static void
mysql_deparse_operator_name(StringInfo buf,Form_pg_operator opform)1113 mysql_deparse_operator_name(StringInfo buf, Form_pg_operator opform)
1114 {
1115 /* opname is not a SQL identifier, so we should not quote it. */
1116 cur_opname = NameStr(opform->oprname);
1117
1118 /* Print schema name only if it's not pg_catalog */
1119 if (opform->oprnamespace != PG_CATALOG_NAMESPACE)
1120 {
1121 const char *opnspname;
1122
1123 opnspname = get_namespace_name(opform->oprnamespace);
1124 /* Print fully qualified operator name. */
1125 appendStringInfo(buf, "OPERATOR(%s.%s)",
1126 mysql_quote_identifier(opnspname, '`'), cur_opname);
1127 }
1128 else
1129 {
1130 if (strcmp(cur_opname, "~~") == 0)
1131 appendStringInfoString(buf, "LIKE BINARY");
1132 else if (strcmp(cur_opname, "~~*") == 0)
1133 appendStringInfoString(buf, "LIKE");
1134 else if (strcmp(cur_opname, "!~~") == 0)
1135 appendStringInfoString(buf, "NOT LIKE BINARY");
1136 else if (strcmp(cur_opname, "!~~*") == 0)
1137 appendStringInfoString(buf, "NOT LIKE");
1138 else if (strcmp(cur_opname, "~") == 0)
1139 appendStringInfoString(buf, "REGEXP BINARY");
1140 else if (strcmp(cur_opname, "~*") == 0)
1141 appendStringInfoString(buf, "REGEXP");
1142 else if (strcmp(cur_opname, "!~") == 0)
1143 appendStringInfoString(buf, "NOT REGEXP BINARY");
1144 else if (strcmp(cur_opname, "!~*") == 0)
1145 appendStringInfoString(buf, "NOT REGEXP");
1146 else
1147 appendStringInfoString(buf, cur_opname);
1148 }
1149 }
1150
1151 /*
1152 * Deparse IS DISTINCT FROM.
1153 */
1154 static void
mysql_deparse_distinct_expr(DistinctExpr * node,deparse_expr_cxt * context)1155 mysql_deparse_distinct_expr(DistinctExpr *node, deparse_expr_cxt *context)
1156 {
1157 StringInfo buf = context->buf;
1158
1159 Assert(list_length(node->args) == 2);
1160
1161 appendStringInfoChar(buf, '(');
1162 deparseExpr(linitial(node->args), context);
1163 appendStringInfoString(buf, " IS DISTINCT FROM ");
1164 deparseExpr(lsecond(node->args), context);
1165 appendStringInfoChar(buf, ')');
1166 }
1167
1168 /*
1169 * Deparse given ScalarArrayOpExpr expression. To avoid problems
1170 * around priority of operations, we always parenthesize the arguments.
1171 */
1172 static void
mysql_deparse_scalar_array_op_expr(ScalarArrayOpExpr * node,deparse_expr_cxt * context)1173 mysql_deparse_scalar_array_op_expr(ScalarArrayOpExpr *node,
1174 deparse_expr_cxt *context)
1175 {
1176 StringInfo buf = context->buf;
1177 HeapTuple tuple;
1178 Expr *arg1;
1179 Expr *arg2;
1180 Form_pg_operator form;
1181 char *opname;
1182 Oid typoutput;
1183 bool typIsVarlena;
1184 char *extval;
1185
1186 /* Retrieve information about the operator from system catalog. */
1187 tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(node->opno));
1188 if (!HeapTupleIsValid(tuple))
1189 elog(ERROR, "cache lookup failed for operator %u", node->opno);
1190 form = (Form_pg_operator) GETSTRUCT(tuple);
1191
1192 /* Sanity check. */
1193 Assert(list_length(node->args) == 2);
1194
1195 /* Deparse left operand. */
1196 arg1 = linitial(node->args);
1197 deparseExpr(arg1, context);
1198 appendStringInfoChar(buf, ' ');
1199
1200 opname = NameStr(form->oprname);
1201 if (strcmp(opname, "<>") == 0)
1202 appendStringInfo(buf, " NOT ");
1203
1204 /* Deparse operator name plus decoration. */
1205 appendStringInfo(buf, " IN (");
1206
1207 /* Deparse right operand. */
1208 arg2 = lsecond(node->args);
1209 switch (nodeTag((Node *) arg2))
1210 {
1211 case T_Const:
1212 {
1213 Const *c = (Const *) arg2;
1214
1215 if (c->constisnull)
1216 {
1217 appendStringInfoString(buf, " NULL");
1218 ReleaseSysCache(tuple);
1219 return;
1220 }
1221
1222 getTypeOutputInfo(c->consttype, &typoutput, &typIsVarlena);
1223 extval = OidOutputFunctionCall(typoutput, c->constvalue);
1224
1225 switch (c->consttype)
1226 {
1227 case INT4ARRAYOID:
1228 case OIDARRAYOID:
1229 mysql_deparse_string(buf, extval, false);
1230 break;
1231 default:
1232 mysql_deparse_string(buf, extval, true);
1233 break;
1234 }
1235 }
1236 break;
1237 default:
1238 deparseExpr(arg2, context);
1239 break;
1240 }
1241 appendStringInfoChar(buf, ')');
1242
1243 ReleaseSysCache(tuple);
1244 }
1245
1246 /*
1247 * Deparse a RelabelType (binary-compatible cast) node.
1248 */
1249 static void
mysql_deparse_relabel_type(RelabelType * node,deparse_expr_cxt * context)1250 mysql_deparse_relabel_type(RelabelType *node, deparse_expr_cxt *context)
1251 {
1252 deparseExpr(node->arg, context);
1253 }
1254
1255 /*
1256 * Deparse a BoolExpr node.
1257 *
1258 * Note: by the time we get here, AND and OR expressions have been flattened
1259 * into N-argument form, so we'd better be prepared to deal with that.
1260 */
1261 static void
mysql_deparse_bool_expr(BoolExpr * node,deparse_expr_cxt * context)1262 mysql_deparse_bool_expr(BoolExpr *node, deparse_expr_cxt *context)
1263 {
1264 StringInfo buf = context->buf;
1265 const char *op = NULL; /* keep compiler quiet */
1266 bool first;
1267 ListCell *lc;
1268
1269 switch (node->boolop)
1270 {
1271 case AND_EXPR:
1272 op = "AND";
1273 break;
1274 case OR_EXPR:
1275 op = "OR";
1276 break;
1277 case NOT_EXPR:
1278 appendStringInfoChar(buf, '(');
1279 appendStringInfoString(buf, "NOT ");
1280 deparseExpr(linitial(node->args), context);
1281 appendStringInfoChar(buf, ')');
1282 return;
1283 }
1284
1285 appendStringInfoChar(buf, '(');
1286 first = true;
1287 foreach(lc, node->args)
1288 {
1289 if (!first)
1290 appendStringInfo(buf, " %s ", op);
1291 deparseExpr((Expr *) lfirst(lc), context);
1292 first = false;
1293 }
1294 appendStringInfoChar(buf, ')');
1295 }
1296
1297 /*
1298 * Deparse IS [NOT] NULL expression.
1299 */
1300 static void
mysql_deparse_null_test(NullTest * node,deparse_expr_cxt * context)1301 mysql_deparse_null_test(NullTest *node, deparse_expr_cxt *context)
1302 {
1303 StringInfo buf = context->buf;
1304
1305 appendStringInfoChar(buf, '(');
1306 deparseExpr(node->arg, context);
1307 if (node->nulltesttype == IS_NULL)
1308 appendStringInfoString(buf, " IS NULL");
1309 else
1310 appendStringInfoString(buf, " IS NOT NULL");
1311 appendStringInfoChar(buf, ')');
1312 }
1313
1314 /*
1315 * Deparse ARRAY[...] construct.
1316 */
1317 static void
mysql_deparse_array_expr(ArrayExpr * node,deparse_expr_cxt * context)1318 mysql_deparse_array_expr(ArrayExpr *node, deparse_expr_cxt *context)
1319 {
1320 StringInfo buf = context->buf;
1321 bool first = true;
1322 ListCell *lc;
1323
1324 appendStringInfoString(buf, "ARRAY[");
1325 foreach(lc, node->elements)
1326 {
1327 if (!first)
1328 appendStringInfoString(buf, ", ");
1329 deparseExpr(lfirst(lc), context);
1330 first = false;
1331 }
1332 appendStringInfoChar(buf, ']');
1333 }
1334
1335 /*
1336 * Print the representation of a parameter to be sent to the remote side.
1337 *
1338 * Note: we always label the Param's type explicitly rather than relying on
1339 * transmitting a numeric type OID in PQexecParams(). This allows us to
1340 * avoid assuming that types have the same OIDs on the remote side as they
1341 * do locally --- they need only have the same names.
1342 */
1343 static void
mysql_print_remote_param(int paramindex,Oid paramtype,int32 paramtypmod,deparse_expr_cxt * context)1344 mysql_print_remote_param(int paramindex, Oid paramtype, int32 paramtypmod,
1345 deparse_expr_cxt *context)
1346 {
1347 StringInfo buf = context->buf;
1348
1349 appendStringInfo(buf, "?");
1350 }
1351
1352 static void
mysql_print_remote_placeholder(Oid paramtype,int32 paramtypmod,deparse_expr_cxt * context)1353 mysql_print_remote_placeholder(Oid paramtype, int32 paramtypmod,
1354 deparse_expr_cxt *context)
1355 {
1356 StringInfo buf = context->buf;
1357
1358 appendStringInfo(buf, "(SELECT null)");
1359 }
1360
1361 /*
1362 * Return true if given object is one of PostgreSQL's built-in objects.
1363 *
1364 * We use FirstBootstrapObjectId as the cutoff, so that we only consider
1365 * objects with hand-assigned OIDs to be "built in", not for instance any
1366 * function or type defined in the information_schema.
1367 *
1368 * Our constraints for dealing with types are tighter than they are for
1369 * functions or operators: we want to accept only types that are in pg_catalog,
1370 * else format_type might incorrectly fail to schema-qualify their names.
1371 * (This could be fixed with some changes to format_type, but for now there's
1372 * no need.) Thus we must exclude information_schema types.
1373 *
1374 * XXX there is a problem with this, which is that the set of built-in
1375 * objects expands over time. Something that is built-in to us might not
1376 * be known to the remote server, if it's of an older version. But keeping
1377 * track of that would be a huge exercise.
1378 */
1379 static bool
is_builtin(Oid oid)1380 is_builtin(Oid oid)
1381 {
1382 return (oid < FirstBootstrapObjectId);
1383 }
1384
1385 /*
1386 * Check if expression is safe to execute remotely, and return true if so.
1387 *
1388 * In addition, *outer_cxt is updated with collation information.
1389 *
1390 * We must check that the expression contains only node types we can deparse,
1391 * that all types/functions/operators are safe to send (which we approximate
1392 * as being built-in), and that all collations used in the expression derive
1393 * from Vars of the foreign table. Because of the latter, the logic is pretty
1394 * close to assign_collations_walker() in parse_collate.c, though we can assume
1395 * here that the given expression is valid.
1396 */
1397 static bool
foreign_expr_walker(Node * node,foreign_glob_cxt * glob_cxt,foreign_loc_cxt * outer_cxt)1398 foreign_expr_walker(Node *node, foreign_glob_cxt *glob_cxt,
1399 foreign_loc_cxt *outer_cxt)
1400 {
1401 bool check_type = true;
1402 foreign_loc_cxt inner_cxt;
1403 Oid collation;
1404 FDWCollateState state;
1405
1406 /* Need do nothing for empty subexpressions */
1407 if (node == NULL)
1408 return true;
1409
1410 /* Set up inner_cxt for possible recursion to child nodes */
1411 inner_cxt.collation = InvalidOid;
1412 inner_cxt.state = FDW_COLLATE_NONE;
1413
1414 switch (nodeTag(node))
1415 {
1416 case T_Var:
1417 {
1418 Var *var = (Var *) node;
1419
1420 /*
1421 * If the Var is from the foreign table, we consider its
1422 * collation (if any) safe to use. If it is from another
1423 * table, we treat its collation the same way as we would a
1424 * Param's collation, i.e. it's not safe for it to have a
1425 * non-default collation.
1426 */
1427 if (bms_is_member(var->varno, glob_cxt->foreignrel->relids) &&
1428 var->varlevelsup == 0)
1429 {
1430 /* Var belongs to foreign table */
1431 collation = var->varcollid;
1432 state = OidIsValid(collation) ? FDW_COLLATE_SAFE : FDW_COLLATE_NONE;
1433 }
1434 else
1435 {
1436 /* Var belongs to some other table */
1437 if (var->varcollid != InvalidOid &&
1438 var->varcollid != DEFAULT_COLLATION_OID)
1439 return false;
1440
1441 /* We can consider that it doesn't set collation */
1442 collation = InvalidOid;
1443 state = FDW_COLLATE_NONE;
1444 }
1445 }
1446 break;
1447 case T_Const:
1448 {
1449 Const *c = (Const *) node;
1450
1451 /*
1452 * If the constant has non default collation, either it's of a
1453 * non-built in type, or it reflects folding of a CollateExpr;
1454 * either way, it's unsafe to send to the remote.
1455 */
1456 if (c->constcollid != InvalidOid &&
1457 c->constcollid != DEFAULT_COLLATION_OID)
1458 return false;
1459
1460 /* Otherwise, we can consider that it doesn't set collation */
1461 collation = InvalidOid;
1462 state = FDW_COLLATE_NONE;
1463 }
1464 break;
1465 case T_Param:
1466 {
1467 Param *p = (Param *) node;
1468
1469 /*
1470 * Collation rule is same as for Consts and non-foreign Vars.
1471 */
1472 collation = p->paramcollid;
1473 if (collation == InvalidOid ||
1474 collation == DEFAULT_COLLATION_OID)
1475 state = FDW_COLLATE_NONE;
1476 else
1477 state = FDW_COLLATE_UNSAFE;
1478 }
1479 break;
1480 #if PG_VERSION_NUM < 120000
1481 case T_ArrayRef:
1482 {
1483 ArrayRef *ar = (ArrayRef *) node;
1484 #else
1485 case T_SubscriptingRef:
1486 {
1487 SubscriptingRef *ar = (SubscriptingRef *) node;
1488 #endif
1489
1490 /* Should not be in the join clauses of the Join-pushdown */
1491 if (glob_cxt->is_join_cond)
1492 return false;
1493
1494 /* Assignment should not be in restrictions. */
1495 if (ar->refassgnexpr != NULL)
1496 return false;
1497
1498 /*
1499 * Recurse to remaining subexpressions. Since the array
1500 * subscripts must yield (noncollatable) integers, they won't
1501 * affect the inner_cxt state.
1502 */
1503 if (!foreign_expr_walker((Node *) ar->refupperindexpr,
1504 glob_cxt, &inner_cxt))
1505 return false;
1506 if (!foreign_expr_walker((Node *) ar->reflowerindexpr,
1507 glob_cxt, &inner_cxt))
1508 return false;
1509 if (!foreign_expr_walker((Node *) ar->refexpr,
1510 glob_cxt, &inner_cxt))
1511 return false;
1512
1513 /*
1514 * Array subscripting should yield same collation as input,
1515 * but for safety use same logic as for function nodes.
1516 */
1517 collation = ar->refcollid;
1518 if (collation == InvalidOid)
1519 state = FDW_COLLATE_NONE;
1520 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
1521 collation == inner_cxt.collation)
1522 state = FDW_COLLATE_SAFE;
1523 else
1524 state = FDW_COLLATE_UNSAFE;
1525 }
1526 break;
1527 case T_FuncExpr:
1528 {
1529 FuncExpr *fe = (FuncExpr *) node;
1530
1531 /* Should not be in the join clauses of the Join-pushdown */
1532 if (glob_cxt->is_join_cond)
1533 return false;
1534
1535 /*
1536 * If function used by the expression is not built-in, it
1537 * can't be sent to remote because it might have incompatible
1538 * semantics on remote side.
1539 */
1540 if (!is_builtin(fe->funcid))
1541 return false;
1542
1543 /*
1544 * Recurse to input subexpressions.
1545 */
1546 if (!foreign_expr_walker((Node *) fe->args,
1547 glob_cxt, &inner_cxt))
1548 return false;
1549
1550 /*
1551 * If function's input collation is not derived from a foreign
1552 * Var, it can't be sent to remote.
1553 */
1554 if (fe->inputcollid == InvalidOid)
1555 /* OK, inputs are all noncollatable */ ;
1556 else if (inner_cxt.state != FDW_COLLATE_SAFE ||
1557 fe->inputcollid != inner_cxt.collation)
1558 return false;
1559
1560 /*
1561 * Detect whether node is introducing a collation not derived
1562 * from a foreign Var. (If so, we just mark it unsafe for now
1563 * rather than immediately returning false, since the parent
1564 * node might not care.)
1565 */
1566 collation = fe->funccollid;
1567 if (collation == InvalidOid)
1568 state = FDW_COLLATE_NONE;
1569 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
1570 collation == inner_cxt.collation)
1571 state = FDW_COLLATE_SAFE;
1572 else
1573 state = FDW_COLLATE_UNSAFE;
1574 }
1575 break;
1576 case T_OpExpr:
1577 case T_DistinctExpr: /* struct-equivalent to OpExpr */
1578 {
1579 OpExpr *oe = (OpExpr *) node;
1580 const char *operatorName = get_opname(oe->opno);
1581
1582 /*
1583 * Join-pushdown allows only a few operators to be pushed down.
1584 */
1585 if (glob_cxt->is_join_cond &&
1586 (!(strcmp(operatorName, "<") == 0 ||
1587 strcmp(operatorName, ">") == 0 ||
1588 strcmp(operatorName, "<=") == 0 ||
1589 strcmp(operatorName, ">=") == 0 ||
1590 strcmp(operatorName, "<>") == 0 ||
1591 strcmp(operatorName, "=") == 0 ||
1592 strcmp(operatorName, "+") == 0 ||
1593 strcmp(operatorName, "-") == 0 ||
1594 strcmp(operatorName, "*") == 0 ||
1595 strcmp(operatorName, "%") == 0 ||
1596 strcmp(operatorName, "/") == 0)))
1597 return false;
1598
1599 /*
1600 * Similarly, only built-in operators can be sent to remote.
1601 * (If the operator is, surely its underlying function is
1602 * too.)
1603 */
1604 if (!is_builtin(oe->opno))
1605 return false;
1606
1607 /*
1608 * Recurse to input subexpressions.
1609 */
1610 if (!foreign_expr_walker((Node *) oe->args,
1611 glob_cxt, &inner_cxt))
1612 return false;
1613
1614 /*
1615 * If operator's input collation is not derived from a foreign
1616 * Var, it can't be sent to remote.
1617 */
1618 if (oe->inputcollid == InvalidOid)
1619 /* OK, inputs are all noncollatable */ ;
1620 else if (inner_cxt.state != FDW_COLLATE_SAFE ||
1621 oe->inputcollid != inner_cxt.collation)
1622 return false;
1623
1624 /* Result-collation handling is same as for functions */
1625 collation = oe->opcollid;
1626 if (collation == InvalidOid)
1627 state = FDW_COLLATE_NONE;
1628 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
1629 collation == inner_cxt.collation)
1630 state = FDW_COLLATE_SAFE;
1631 else
1632 state = FDW_COLLATE_UNSAFE;
1633 }
1634 break;
1635 case T_ScalarArrayOpExpr:
1636 {
1637 ScalarArrayOpExpr *oe = (ScalarArrayOpExpr *) node;
1638
1639 /* Should not be in the join clauses of the Join-pushdown */
1640 if (glob_cxt->is_join_cond)
1641 return false;
1642
1643 /*
1644 * Again, only built-in operators can be sent to remote.
1645 */
1646 if (!is_builtin(oe->opno))
1647 return false;
1648
1649 /*
1650 * Recurse to input subexpressions.
1651 */
1652 if (!foreign_expr_walker((Node *) oe->args,
1653 glob_cxt, &inner_cxt))
1654 return false;
1655
1656 /*
1657 * If operator's input collation is not derived from a foreign
1658 * Var, it can't be sent to remote.
1659 */
1660 if (oe->inputcollid == InvalidOid)
1661 /* OK, inputs are all noncollatable */ ;
1662 else if (inner_cxt.state != FDW_COLLATE_SAFE ||
1663 oe->inputcollid != inner_cxt.collation)
1664 return false;
1665
1666 /* Output is always boolean and so noncollatable. */
1667 collation = InvalidOid;
1668 state = FDW_COLLATE_NONE;
1669 }
1670 break;
1671 case T_RelabelType:
1672 {
1673 RelabelType *r = (RelabelType *) node;
1674
1675 /*
1676 * Recurse to input subexpression.
1677 */
1678 if (!foreign_expr_walker((Node *) r->arg,
1679 glob_cxt, &inner_cxt))
1680 return false;
1681
1682 /*
1683 * RelabelType must not introduce a collation not derived from
1684 * an input foreign Var.
1685 */
1686 collation = r->resultcollid;
1687 if (collation == InvalidOid)
1688 state = FDW_COLLATE_NONE;
1689 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
1690 collation == inner_cxt.collation)
1691 state = FDW_COLLATE_SAFE;
1692 else
1693 state = FDW_COLLATE_UNSAFE;
1694 }
1695 break;
1696 case T_BoolExpr:
1697 {
1698 BoolExpr *b = (BoolExpr *) node;
1699
1700 /*
1701 * Recurse to input subexpressions.
1702 */
1703 if (!foreign_expr_walker((Node *) b->args,
1704 glob_cxt, &inner_cxt))
1705 return false;
1706
1707 /* Output is always boolean and so noncollatable. */
1708 collation = InvalidOid;
1709 state = FDW_COLLATE_NONE;
1710 }
1711 break;
1712 case T_NullTest:
1713 {
1714 NullTest *nt = (NullTest *) node;
1715
1716 /*
1717 * Recurse to input subexpressions.
1718 */
1719 if (!foreign_expr_walker((Node *) nt->arg,
1720 glob_cxt, &inner_cxt))
1721 return false;
1722
1723 /* Output is always boolean and so noncollatable. */
1724 collation = InvalidOid;
1725 state = FDW_COLLATE_NONE;
1726 }
1727 break;
1728 case T_ArrayExpr:
1729 {
1730 ArrayExpr *a = (ArrayExpr *) node;
1731
1732 /* Should not be in the join clauses of the Join-pushdown */
1733 if (glob_cxt->is_join_cond)
1734 return false;
1735
1736 /*
1737 * Recurse to input subexpressions.
1738 */
1739 if (!foreign_expr_walker((Node *) a->elements,
1740 glob_cxt, &inner_cxt))
1741 return false;
1742
1743 /*
1744 * ArrayExpr must not introduce a collation not derived from
1745 * an input foreign Var.
1746 */
1747 collation = a->array_collid;
1748 if (collation == InvalidOid)
1749 state = FDW_COLLATE_NONE;
1750 else if (inner_cxt.state == FDW_COLLATE_SAFE &&
1751 collation == inner_cxt.collation)
1752 state = FDW_COLLATE_SAFE;
1753 else
1754 state = FDW_COLLATE_UNSAFE;
1755 }
1756 break;
1757 case T_List:
1758 {
1759 List *l = (List *) node;
1760 ListCell *lc;
1761
1762 /*
1763 * Recurse to component subexpressions.
1764 */
1765 foreach(lc, l)
1766 {
1767 if (!foreign_expr_walker((Node *) lfirst(lc),
1768 glob_cxt, &inner_cxt))
1769 return false;
1770 }
1771
1772 /*
1773 * When processing a list, collation state just bubbles up
1774 * from the list elements.
1775 */
1776 collation = inner_cxt.collation;
1777 state = inner_cxt.state;
1778
1779 /* Don't apply exprType() to the list. */
1780 check_type = false;
1781 }
1782 break;
1783 default:
1784
1785 /*
1786 * If it's anything else, assume it's unsafe. This list can be
1787 * expanded later, but don't forget to add deparse support below.
1788 */
1789 return false;
1790 }
1791
1792 /*
1793 * If result type of given expression is not built-in, it can't be sent to
1794 * remote because it might have incompatible semantics on remote side.
1795 */
1796 if (check_type && !is_builtin(exprType(node)))
1797 return false;
1798
1799 /*
1800 * Now, merge my collation information into my parent's state.
1801 */
1802 if (state > outer_cxt->state)
1803 {
1804 /* Override previous parent state */
1805 outer_cxt->collation = collation;
1806 outer_cxt->state = state;
1807 }
1808 else if (state == outer_cxt->state)
1809 {
1810 /* Merge, or detect error if there's a collation conflict */
1811 switch (state)
1812 {
1813 case FDW_COLLATE_NONE:
1814 /* Nothing + nothing is still nothing */
1815 break;
1816 case FDW_COLLATE_SAFE:
1817 if (collation != outer_cxt->collation)
1818 {
1819 /*
1820 * Non-default collation always beats default.
1821 */
1822 if (outer_cxt->collation == DEFAULT_COLLATION_OID)
1823 {
1824 /* Override previous parent state */
1825 outer_cxt->collation = collation;
1826 }
1827 else if (collation != DEFAULT_COLLATION_OID)
1828 {
1829 /*
1830 * Conflict; show state as indeterminate. We don't
1831 * want to "return false" right away, since parent
1832 * node might not care about collation.
1833 */
1834 outer_cxt->state = FDW_COLLATE_UNSAFE;
1835 }
1836 }
1837 break;
1838 case FDW_COLLATE_UNSAFE:
1839 /* We're still conflicted ... */
1840 break;
1841 }
1842 }
1843
1844 /* It looks OK */
1845 return true;
1846 }
1847
1848 /*
1849 * Returns true if given expr is safe to evaluate on the foreign server.
1850 */
1851 bool
1852 mysql_is_foreign_expr(PlannerInfo *root, RelOptInfo *baserel, Expr *expr,
1853 bool is_join_cond)
1854 {
1855 foreign_glob_cxt glob_cxt;
1856 foreign_loc_cxt loc_cxt;
1857
1858 /*
1859 * Check that the expression consists of nodes that are safe to execute
1860 * remotely.
1861 */
1862 glob_cxt.root = root;
1863 glob_cxt.foreignrel = baserel;
1864 glob_cxt.is_join_cond = is_join_cond;
1865 loc_cxt.collation = InvalidOid;
1866 loc_cxt.state = FDW_COLLATE_NONE;
1867 if (!foreign_expr_walker((Node *) expr, &glob_cxt, &loc_cxt))
1868 return false;
1869
1870 /* Expressions examined here should be boolean, ie noncollatable */
1871 Assert(loc_cxt.collation == InvalidOid);
1872 Assert(loc_cxt.state == FDW_COLLATE_NONE);
1873
1874 /* OK to evaluate on the remote server */
1875 return true;
1876 }
1877
1878 /*
1879 * mysql_append_conditions
1880 * Deparse conditions from the provided list and append them to buf.
1881 *
1882 * The conditions in the list are assumed to be ANDed.
1883 *
1884 * Depending on the caller, the list elements might be either RestrictInfos
1885 * or bare clauses.
1886 */
1887 static void
1888 mysql_append_conditions(List *exprs, deparse_expr_cxt *context)
1889 {
1890 ListCell *lc;
1891 bool is_first = true;
1892 StringInfo buf = context->buf;
1893
1894 foreach(lc, exprs)
1895 {
1896 Expr *expr = (Expr *) lfirst(lc);
1897
1898 /*
1899 * Extract clause from RestrictInfo, if required. See comments in
1900 * declaration of MySQLFdwRelationInfo for details.
1901 */
1902 if (IsA(expr, RestrictInfo))
1903 {
1904 RestrictInfo *ri = (RestrictInfo *) expr;
1905
1906 expr = ri->clause;
1907 }
1908
1909 /* Connect expressions with "AND" and parenthesize each condition. */
1910 if (!is_first)
1911 appendStringInfoString(buf, " AND ");
1912
1913 appendStringInfoChar(buf, '(');
1914 deparseExpr(expr, context);
1915 appendStringInfoChar(buf, ')');
1916
1917 is_first = false;
1918 }
1919 }
1920
1921 /*
1922 * mysql_deparse_from_expr
1923 * Construct a FROM clause
1924 */
1925 void
1926 mysql_deparse_from_expr(StringInfo buf, PlannerInfo *root,
1927 RelOptInfo *foreignrel, bool use_alias,
1928 List **params_list)
1929 {
1930 MySQLFdwRelationInfo *fpinfo = (MySQLFdwRelationInfo *) foreignrel->fdw_private;
1931
1932 #if PG_VERSION_NUM >= 100000
1933 if (IS_JOIN_REL(foreignrel))
1934 #else
1935 if (foreignrel->reloptkind == RELOPT_JOINREL)
1936 #endif
1937 {
1938 RelOptInfo *rel_o = fpinfo->outerrel;
1939 RelOptInfo *rel_i = fpinfo->innerrel;
1940 StringInfoData join_sql_o;
1941 StringInfoData join_sql_i;
1942
1943 /* Deparse outer relation */
1944 initStringInfo(&join_sql_o);
1945 mysql_deparse_from_expr(&join_sql_o, root, rel_o, true, params_list);
1946
1947 /* Deparse inner relation */
1948 initStringInfo(&join_sql_i);
1949 mysql_deparse_from_expr(&join_sql_i, root, rel_i, true, params_list);
1950
1951 /*
1952 * For a join relation FROM clause entry is deparsed as
1953 *
1954 * ((outer relation) <join type> (inner relation) ON (joinclauses)
1955 */
1956 appendStringInfo(buf, "(%s %s JOIN %s ON ", join_sql_o.data,
1957 mysql_get_jointype_name(fpinfo->jointype),
1958 join_sql_i.data);
1959
1960 /* Append join clause; (TRUE) if no join clause */
1961 if (fpinfo->joinclauses)
1962 {
1963 deparse_expr_cxt context;
1964
1965 context.buf = buf;
1966 context.foreignrel = foreignrel;
1967 context.root = root;
1968 context.params_list = params_list;
1969
1970 appendStringInfo(buf, "(");
1971 mysql_append_conditions(fpinfo->joinclauses, &context);
1972 appendStringInfo(buf, ")");
1973 }
1974 else
1975 appendStringInfoString(buf, "(TRUE)");
1976
1977 /* End the FROM clause entry. */
1978 appendStringInfo(buf, ")");
1979 }
1980 else
1981 {
1982 RangeTblEntry *rte = planner_rt_fetch(foreignrel->relid, root);
1983 Relation rel;
1984
1985 /*
1986 * Core code already has some lock on each rel being planned, so we can
1987 * use NoLock here.
1988 */
1989 #if PG_VERSION_NUM < 130000
1990 rel = heap_open(rte->relid, NoLock);
1991 #else
1992 rel = table_open(rte->relid, NoLock);
1993 #endif
1994
1995 mysql_deparse_relation(buf, rel);
1996
1997 /*
1998 * Add a unique alias to avoid any conflict in relation names due to
1999 * pulled up subqueries in the query being built for a pushed down
2000 * join.
2001 */
2002 if (use_alias)
2003 appendStringInfo(buf, " %s%d", REL_ALIAS_PREFIX,
2004 foreignrel->relid);
2005
2006 #if PG_VERSION_NUM < 130000
2007 heap_close(rel, NoLock);
2008 #else
2009 table_close(rel, NoLock);
2010 #endif
2011 }
2012 return;
2013 }
2014
2015 /*
2016 * mysql_get_jointype_name
2017 * Output join name for given join type
2018 */
2019 extern const char *
2020 mysql_get_jointype_name(JoinType jointype)
2021 {
2022 switch (jointype)
2023 {
2024 case JOIN_INNER:
2025 return "INNER";
2026
2027 case JOIN_LEFT:
2028 return "LEFT";
2029
2030 case JOIN_RIGHT:
2031 return "RIGHT";
2032
2033 default:
2034 /* Shouldn't come here, but protect from buggy code. */
2035 elog(ERROR, "unsupported join type %d", jointype);
2036 }
2037
2038 /* Keep compiler happy */
2039 return NULL;
2040 }
2041