1 /*-------------------------------------------------------------------------
2  *
3  * pseudotypes.c
4  *	  Functions for the system pseudo-types.
5  *
6  * A pseudo-type isn't really a type and never has any operations, but
7  * we do need to supply input and output functions to satisfy the links
8  * in the pseudo-type's entry in pg_type.  In most cases the functions
9  * just throw an error if invoked.  (XXX the error messages here cover
10  * the most common case, but might be confusing in some contexts.  Can
11  * we do better?)
12  *
13  *
14  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
15  * Portions Copyright (c) 1994, Regents of the University of California
16  *
17  *
18  * IDENTIFICATION
19  *	  src/backend/utils/adt/pseudotypes.c
20  *
21  *-------------------------------------------------------------------------
22  */
23 #include "postgres.h"
24 
25 #include "libpq/pqformat.h"
26 #include "utils/array.h"
27 #include "utils/builtins.h"
28 #include "utils/rangetypes.h"
29 
30 
31 /*
32  * cstring_in		- input routine for pseudo-type CSTRING.
33  *
34  * We might as well allow this to support constructs like "foo_in('blah')".
35  */
36 Datum
cstring_in(PG_FUNCTION_ARGS)37 cstring_in(PG_FUNCTION_ARGS)
38 {
39 	char	   *str = PG_GETARG_CSTRING(0);
40 
41 	PG_RETURN_CSTRING(pstrdup(str));
42 }
43 
44 /*
45  * cstring_out		- output routine for pseudo-type CSTRING.
46  *
47  * We allow this mainly so that "SELECT some_output_function(...)" does
48  * what the user will expect.
49  */
50 Datum
cstring_out(PG_FUNCTION_ARGS)51 cstring_out(PG_FUNCTION_ARGS)
52 {
53 	char	   *str = PG_GETARG_CSTRING(0);
54 
55 	PG_RETURN_CSTRING(pstrdup(str));
56 }
57 
58 /*
59  * cstring_recv		- binary input routine for pseudo-type CSTRING.
60  */
61 Datum
cstring_recv(PG_FUNCTION_ARGS)62 cstring_recv(PG_FUNCTION_ARGS)
63 {
64 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
65 	char	   *str;
66 	int			nbytes;
67 
68 	str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
69 	PG_RETURN_CSTRING(str);
70 }
71 
72 /*
73  * cstring_send		- binary output routine for pseudo-type CSTRING.
74  */
75 Datum
cstring_send(PG_FUNCTION_ARGS)76 cstring_send(PG_FUNCTION_ARGS)
77 {
78 	char	   *str = PG_GETARG_CSTRING(0);
79 	StringInfoData buf;
80 
81 	pq_begintypsend(&buf);
82 	pq_sendtext(&buf, str, strlen(str));
83 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
84 }
85 
86 
87 /*
88  * any_in		- input routine for pseudo-type ANY.
89  */
90 Datum
any_in(PG_FUNCTION_ARGS)91 any_in(PG_FUNCTION_ARGS)
92 {
93 	ereport(ERROR,
94 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
95 			 errmsg("cannot accept a value of type any")));
96 
97 	PG_RETURN_VOID();			/* keep compiler quiet */
98 }
99 
100 /*
101  * any_out		- output routine for pseudo-type ANY.
102  */
103 Datum
any_out(PG_FUNCTION_ARGS)104 any_out(PG_FUNCTION_ARGS)
105 {
106 	ereport(ERROR,
107 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
108 			 errmsg("cannot display a value of type any")));
109 
110 	PG_RETURN_VOID();			/* keep compiler quiet */
111 }
112 
113 
114 /*
115  * anyarray_in		- input routine for pseudo-type ANYARRAY.
116  */
117 Datum
anyarray_in(PG_FUNCTION_ARGS)118 anyarray_in(PG_FUNCTION_ARGS)
119 {
120 	ereport(ERROR,
121 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
122 			 errmsg("cannot accept a value of type anyarray")));
123 
124 	PG_RETURN_VOID();			/* keep compiler quiet */
125 }
126 
127 /*
128  * anyarray_out		- output routine for pseudo-type ANYARRAY.
129  *
130  * We may as well allow this, since array_out will in fact work.
131  */
132 Datum
anyarray_out(PG_FUNCTION_ARGS)133 anyarray_out(PG_FUNCTION_ARGS)
134 {
135 	return array_out(fcinfo);
136 }
137 
138 /*
139  * anyarray_recv		- binary input routine for pseudo-type ANYARRAY.
140  *
141  * XXX this could actually be made to work, since the incoming array
142  * data will contain the element type OID.  Need to think through
143  * type-safety issues before allowing it, however.
144  */
145 Datum
anyarray_recv(PG_FUNCTION_ARGS)146 anyarray_recv(PG_FUNCTION_ARGS)
147 {
148 	ereport(ERROR,
149 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
150 			 errmsg("cannot accept a value of type anyarray")));
151 
152 	PG_RETURN_VOID();			/* keep compiler quiet */
153 }
154 
155 /*
156  * anyarray_send		- binary output routine for pseudo-type ANYARRAY.
157  *
158  * We may as well allow this, since array_send will in fact work.
159  */
160 Datum
anyarray_send(PG_FUNCTION_ARGS)161 anyarray_send(PG_FUNCTION_ARGS)
162 {
163 	return array_send(fcinfo);
164 }
165 
166 
167 /*
168  * anyenum_in		- input routine for pseudo-type ANYENUM.
169  */
170 Datum
anyenum_in(PG_FUNCTION_ARGS)171 anyenum_in(PG_FUNCTION_ARGS)
172 {
173 	ereport(ERROR,
174 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
175 			 errmsg("cannot accept a value of type anyenum")));
176 
177 	PG_RETURN_VOID();			/* keep compiler quiet */
178 }
179 
180 /*
181  * anyenum_out		- output routine for pseudo-type ANYENUM.
182  *
183  * We may as well allow this, since enum_out will in fact work.
184  */
185 Datum
anyenum_out(PG_FUNCTION_ARGS)186 anyenum_out(PG_FUNCTION_ARGS)
187 {
188 	return enum_out(fcinfo);
189 }
190 
191 /*
192  * anyrange_in		- input routine for pseudo-type ANYRANGE.
193  */
194 Datum
anyrange_in(PG_FUNCTION_ARGS)195 anyrange_in(PG_FUNCTION_ARGS)
196 {
197 	ereport(ERROR,
198 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
199 			 errmsg("cannot accept a value of type anyrange")));
200 
201 	PG_RETURN_VOID();			/* keep compiler quiet */
202 }
203 
204 /*
205  * anyrange_out		- output routine for pseudo-type ANYRANGE.
206  *
207  * We may as well allow this, since range_out will in fact work.
208  */
209 Datum
anyrange_out(PG_FUNCTION_ARGS)210 anyrange_out(PG_FUNCTION_ARGS)
211 {
212 	return range_out(fcinfo);
213 }
214 
215 /*
216  * void_in		- input routine for pseudo-type VOID.
217  *
218  * We allow this so that PL functions can return VOID without any special
219  * hack in the PL handler.  Whatever value the PL thinks it's returning
220  * will just be ignored.
221  */
222 Datum
void_in(PG_FUNCTION_ARGS)223 void_in(PG_FUNCTION_ARGS)
224 {
225 	PG_RETURN_VOID();			/* you were expecting something different? */
226 }
227 
228 /*
229  * void_out		- output routine for pseudo-type VOID.
230  *
231  * We allow this so that "SELECT function_returning_void(...)" works.
232  */
233 Datum
void_out(PG_FUNCTION_ARGS)234 void_out(PG_FUNCTION_ARGS)
235 {
236 	PG_RETURN_CSTRING(pstrdup(""));
237 }
238 
239 /*
240  * void_recv	- binary input routine for pseudo-type VOID.
241  *
242  * Note that since we consume no bytes, an attempt to send anything but
243  * an empty string will result in an "invalid message format" error.
244  */
245 Datum
void_recv(PG_FUNCTION_ARGS)246 void_recv(PG_FUNCTION_ARGS)
247 {
248 	PG_RETURN_VOID();
249 }
250 
251 /*
252  * void_send	- binary output routine for pseudo-type VOID.
253  *
254  * We allow this so that "SELECT function_returning_void(...)" works
255  * even when binary output is requested.
256  */
257 Datum
void_send(PG_FUNCTION_ARGS)258 void_send(PG_FUNCTION_ARGS)
259 {
260 	StringInfoData buf;
261 
262 	/* send an empty string */
263 	pq_begintypsend(&buf);
264 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
265 }
266 
267 
268 /*
269  * trigger_in		- input routine for pseudo-type TRIGGER.
270  */
271 Datum
trigger_in(PG_FUNCTION_ARGS)272 trigger_in(PG_FUNCTION_ARGS)
273 {
274 	ereport(ERROR,
275 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
276 			 errmsg("cannot accept a value of type trigger")));
277 
278 	PG_RETURN_VOID();			/* keep compiler quiet */
279 }
280 
281 /*
282  * trigger_out		- output routine for pseudo-type TRIGGER.
283  */
284 Datum
trigger_out(PG_FUNCTION_ARGS)285 trigger_out(PG_FUNCTION_ARGS)
286 {
287 	ereport(ERROR,
288 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
289 			 errmsg("cannot display a value of type trigger")));
290 
291 	PG_RETURN_VOID();			/* keep compiler quiet */
292 }
293 
294 
295 /*
296  * event_trigger_in - input routine for pseudo-type event_trigger.
297  */
298 Datum
event_trigger_in(PG_FUNCTION_ARGS)299 event_trigger_in(PG_FUNCTION_ARGS)
300 {
301 	ereport(ERROR,
302 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
303 			 errmsg("cannot accept a value of type event_trigger")));
304 
305 	PG_RETURN_VOID();			/* keep compiler quiet */
306 }
307 
308 /*
309  * event_trigger_out - output routine for pseudo-type event_trigger.
310  */
311 Datum
event_trigger_out(PG_FUNCTION_ARGS)312 event_trigger_out(PG_FUNCTION_ARGS)
313 {
314 	ereport(ERROR,
315 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
316 			 errmsg("cannot display a value of type event_trigger")));
317 
318 	PG_RETURN_VOID();			/* keep compiler quiet */
319 }
320 
321 
322 /*
323  * language_handler_in		- input routine for pseudo-type LANGUAGE_HANDLER.
324  */
325 Datum
language_handler_in(PG_FUNCTION_ARGS)326 language_handler_in(PG_FUNCTION_ARGS)
327 {
328 	ereport(ERROR,
329 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
330 			 errmsg("cannot accept a value of type language_handler")));
331 
332 	PG_RETURN_VOID();			/* keep compiler quiet */
333 }
334 
335 /*
336  * language_handler_out		- output routine for pseudo-type LANGUAGE_HANDLER.
337  */
338 Datum
language_handler_out(PG_FUNCTION_ARGS)339 language_handler_out(PG_FUNCTION_ARGS)
340 {
341 	ereport(ERROR,
342 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
343 			 errmsg("cannot display a value of type language_handler")));
344 
345 	PG_RETURN_VOID();			/* keep compiler quiet */
346 }
347 
348 
349 /*
350  * fdw_handler_in		- input routine for pseudo-type FDW_HANDLER.
351  */
352 Datum
fdw_handler_in(PG_FUNCTION_ARGS)353 fdw_handler_in(PG_FUNCTION_ARGS)
354 {
355 	ereport(ERROR,
356 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
357 			 errmsg("cannot accept a value of type fdw_handler")));
358 
359 	PG_RETURN_VOID();			/* keep compiler quiet */
360 }
361 
362 /*
363  * fdw_handler_out		- output routine for pseudo-type FDW_HANDLER.
364  */
365 Datum
fdw_handler_out(PG_FUNCTION_ARGS)366 fdw_handler_out(PG_FUNCTION_ARGS)
367 {
368 	ereport(ERROR,
369 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
370 			 errmsg("cannot display a value of type fdw_handler")));
371 
372 	PG_RETURN_VOID();			/* keep compiler quiet */
373 }
374 
375 
376 /*
377  * index_am_handler_in		- input routine for pseudo-type INDEX_AM_HANDLER.
378  */
379 Datum
index_am_handler_in(PG_FUNCTION_ARGS)380 index_am_handler_in(PG_FUNCTION_ARGS)
381 {
382 	ereport(ERROR,
383 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
384 			 errmsg("cannot accept a value of type index_am_handler")));
385 
386 	PG_RETURN_VOID();			/* keep compiler quiet */
387 }
388 
389 /*
390  * index_am_handler_out		- output routine for pseudo-type INDEX_AM_HANDLER.
391  */
392 Datum
index_am_handler_out(PG_FUNCTION_ARGS)393 index_am_handler_out(PG_FUNCTION_ARGS)
394 {
395 	ereport(ERROR,
396 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
397 			 errmsg("cannot display a value of type index_am_handler")));
398 
399 	PG_RETURN_VOID();			/* keep compiler quiet */
400 }
401 
402 
403 /*
404  * tsm_handler_in		- input routine for pseudo-type TSM_HANDLER.
405  */
406 Datum
tsm_handler_in(PG_FUNCTION_ARGS)407 tsm_handler_in(PG_FUNCTION_ARGS)
408 {
409 	ereport(ERROR,
410 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
411 			 errmsg("cannot accept a value of type tsm_handler")));
412 
413 	PG_RETURN_VOID();			/* keep compiler quiet */
414 }
415 
416 /*
417  * tsm_handler_out		- output routine for pseudo-type TSM_HANDLER.
418  */
419 Datum
tsm_handler_out(PG_FUNCTION_ARGS)420 tsm_handler_out(PG_FUNCTION_ARGS)
421 {
422 	ereport(ERROR,
423 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
424 			 errmsg("cannot display a value of type tsm_handler")));
425 
426 	PG_RETURN_VOID();			/* keep compiler quiet */
427 }
428 
429 
430 /*
431  * internal_in		- input routine for pseudo-type INTERNAL.
432  */
433 Datum
internal_in(PG_FUNCTION_ARGS)434 internal_in(PG_FUNCTION_ARGS)
435 {
436 	ereport(ERROR,
437 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
438 			 errmsg("cannot accept a value of type internal")));
439 
440 	PG_RETURN_VOID();			/* keep compiler quiet */
441 }
442 
443 /*
444  * internal_out		- output routine for pseudo-type INTERNAL.
445  */
446 Datum
internal_out(PG_FUNCTION_ARGS)447 internal_out(PG_FUNCTION_ARGS)
448 {
449 	ereport(ERROR,
450 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
451 			 errmsg("cannot display a value of type internal")));
452 
453 	PG_RETURN_VOID();			/* keep compiler quiet */
454 }
455 
456 
457 /*
458  * opaque_in		- input routine for pseudo-type OPAQUE.
459  */
460 Datum
opaque_in(PG_FUNCTION_ARGS)461 opaque_in(PG_FUNCTION_ARGS)
462 {
463 	ereport(ERROR,
464 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
465 			 errmsg("cannot accept a value of type opaque")));
466 
467 	PG_RETURN_VOID();			/* keep compiler quiet */
468 }
469 
470 /*
471  * opaque_out		- output routine for pseudo-type OPAQUE.
472  */
473 Datum
opaque_out(PG_FUNCTION_ARGS)474 opaque_out(PG_FUNCTION_ARGS)
475 {
476 	ereport(ERROR,
477 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
478 			 errmsg("cannot display a value of type opaque")));
479 
480 	PG_RETURN_VOID();			/* keep compiler quiet */
481 }
482 
483 
484 /*
485  * anyelement_in		- input routine for pseudo-type ANYELEMENT.
486  */
487 Datum
anyelement_in(PG_FUNCTION_ARGS)488 anyelement_in(PG_FUNCTION_ARGS)
489 {
490 	ereport(ERROR,
491 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
492 			 errmsg("cannot accept a value of type anyelement")));
493 
494 	PG_RETURN_VOID();			/* keep compiler quiet */
495 }
496 
497 /*
498  * anyelement_out		- output routine for pseudo-type ANYELEMENT.
499  */
500 Datum
anyelement_out(PG_FUNCTION_ARGS)501 anyelement_out(PG_FUNCTION_ARGS)
502 {
503 	ereport(ERROR,
504 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
505 			 errmsg("cannot display a value of type anyelement")));
506 
507 	PG_RETURN_VOID();			/* keep compiler quiet */
508 }
509 
510 /*
511  * anynonarray_in		- input routine for pseudo-type ANYNONARRAY.
512  */
513 Datum
anynonarray_in(PG_FUNCTION_ARGS)514 anynonarray_in(PG_FUNCTION_ARGS)
515 {
516 	ereport(ERROR,
517 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
518 			 errmsg("cannot accept a value of type anynonarray")));
519 
520 	PG_RETURN_VOID();			/* keep compiler quiet */
521 }
522 
523 /*
524  * anynonarray_out		- output routine for pseudo-type ANYNONARRAY.
525  */
526 Datum
anynonarray_out(PG_FUNCTION_ARGS)527 anynonarray_out(PG_FUNCTION_ARGS)
528 {
529 	ereport(ERROR,
530 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
531 			 errmsg("cannot display a value of type anynonarray")));
532 
533 	PG_RETURN_VOID();			/* keep compiler quiet */
534 }
535 
536 /*
537  * shell_in		- input routine for "shell" types (those not yet filled in).
538  */
539 Datum
shell_in(PG_FUNCTION_ARGS)540 shell_in(PG_FUNCTION_ARGS)
541 {
542 	ereport(ERROR,
543 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
544 			 errmsg("cannot accept a value of a shell type")));
545 
546 	PG_RETURN_VOID();			/* keep compiler quiet */
547 }
548 
549 /*
550  * shell_out		- output routine for "shell" types.
551  */
552 Datum
shell_out(PG_FUNCTION_ARGS)553 shell_out(PG_FUNCTION_ARGS)
554 {
555 	ereport(ERROR,
556 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
557 			 errmsg("cannot display a value of a shell type")));
558 
559 	PG_RETURN_VOID();			/* keep compiler quiet */
560 }
561 
562 
563 /*
564  * pg_node_tree_in		- input routine for type PG_NODE_TREE.
565  *
566  * pg_node_tree isn't really a pseudotype --- it's real enough to be a table
567  * column --- but it presently has no operations of its own, and disallows
568  * input too, so its I/O functions seem to fit here as much as anywhere.
569  */
570 Datum
pg_node_tree_in(PG_FUNCTION_ARGS)571 pg_node_tree_in(PG_FUNCTION_ARGS)
572 {
573 	/*
574 	 * We disallow input of pg_node_tree values because the SQL functions that
575 	 * operate on the type are not secure against malformed input.
576 	 */
577 	ereport(ERROR,
578 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
579 			 errmsg("cannot accept a value of type %s", "pg_node_tree")));
580 
581 	PG_RETURN_VOID();			/* keep compiler quiet */
582 }
583 
584 
585 /*
586  * pg_node_tree_out		- output routine for type PG_NODE_TREE.
587  *
588  * The internal representation is the same as TEXT, so just pass it off.
589  */
590 Datum
pg_node_tree_out(PG_FUNCTION_ARGS)591 pg_node_tree_out(PG_FUNCTION_ARGS)
592 {
593 	return textout(fcinfo);
594 }
595 
596 /*
597  * pg_node_tree_recv		- binary input routine for type PG_NODE_TREE.
598  */
599 Datum
pg_node_tree_recv(PG_FUNCTION_ARGS)600 pg_node_tree_recv(PG_FUNCTION_ARGS)
601 {
602 	ereport(ERROR,
603 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
604 			 errmsg("cannot accept a value of type %s", "pg_node_tree")));
605 
606 	PG_RETURN_VOID();			/* keep compiler quiet */
607 }
608 
609 /*
610  * pg_node_tree_send		- binary output routine for type PG_NODE_TREE.
611  */
612 Datum
pg_node_tree_send(PG_FUNCTION_ARGS)613 pg_node_tree_send(PG_FUNCTION_ARGS)
614 {
615 	return textsend(fcinfo);
616 }
617 
618 /*
619  * pg_ddl_command_in	- input routine for type PG_DDL_COMMAND.
620  *
621  * Like pg_node_tree, pg_ddl_command isn't really a pseudotype; it's here for
622  * the same reasons as that one.
623  */
624 Datum
pg_ddl_command_in(PG_FUNCTION_ARGS)625 pg_ddl_command_in(PG_FUNCTION_ARGS)
626 {
627 	/*
628 	 * Disallow input of pg_ddl_command value.
629 	 */
630 	ereport(ERROR,
631 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
632 			 errmsg("cannot accept a value of type %s", "pg_ddl_command")));
633 
634 	PG_RETURN_VOID();			/* keep compiler quiet */
635 }
636 
637 /*
638  * pg_ddl_command_out		- output routine for type PG_DDL_COMMAND.
639  *
640  * We don't have any good way to output this type directly, so punt.
641  */
642 Datum
pg_ddl_command_out(PG_FUNCTION_ARGS)643 pg_ddl_command_out(PG_FUNCTION_ARGS)
644 {
645 	ereport(ERROR,
646 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
647 			 errmsg("cannot output a value of type %s", "pg_ddl_command")));
648 
649 	PG_RETURN_VOID();
650 }
651 
652 /*
653  * pg_ddl_command_recv		- binary input routine for type PG_DDL_COMMAND.
654  */
655 Datum
pg_ddl_command_recv(PG_FUNCTION_ARGS)656 pg_ddl_command_recv(PG_FUNCTION_ARGS)
657 {
658 	ereport(ERROR,
659 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
660 			 errmsg("cannot accept a value of type %s", "pg_ddl_command")));
661 
662 	PG_RETURN_VOID();
663 }
664 
665 /*
666  * pg_ddl_command_send		- binary output routine for type PG_DDL_COMMAND.
667  */
668 Datum
pg_ddl_command_send(PG_FUNCTION_ARGS)669 pg_ddl_command_send(PG_FUNCTION_ARGS)
670 {
671 	ereport(ERROR,
672 			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
673 			 errmsg("cannot output a value of type %s", "pg_ddl_command")));
674 
675 	PG_RETURN_VOID();
676 }
677