]> granicus.if.org Git - postgresql/blob - doc/src/sgml/typeconv.sgml
Markup additions and spell check. (covers User's Guide)
[postgresql] / doc / src / sgml / typeconv.sgml
1 <chapter Id="typeconv">
2 <title>Type Conversion</title>
3
4 <para>
5 <acronym>SQL</acronym> queries can, intentionally or not, require
6 mixing of different data types in the same expression. 
7 <productname>Postgres</productname> has extensive facilities for
8 evaluating mixed-type expressions.
9 </para>
10
11 <para>
12 In many cases a user will not need
13 to understand the details of the type conversion mechanism.
14 However, the implicit conversions done by <productname>Postgres</productname>
15 can affect the results of a query.  When necessary, these results
16 can be tailored by a user or programmer
17 using <emphasis>explicit</emphasis> type coercion.
18 </para>
19
20 <para>
21 This chapter introduces the <productname>Postgres</productname>
22 type conversion mechanisms and conventions.
23 Refer to the relevant sections in the User's Guide and Programmer's Guide
24 for more information on specific data types and allowed functions and
25 operators.
26 </para>
27
28 <para>
29 The Programmer's Guide has more details on the exact algorithms used for
30 implicit type conversion and coercion.
31 </para>
32
33 <sect1 id="typeconv-overview">
34 <title>Overview</title>
35
36 <para>
37 <acronym>SQL</acronym> is a strongly typed language. That is, every data item
38 has an associated data type which determines its behavior and allowed usage.
39 <productname>Postgres</productname> has an extensible type system that is
40 much more general and flexible than other <acronym>RDBMS</acronym> implementations.
41 Hence, most type conversion behavior in <productname>Postgres</productname>
42 should be governed by general rules rather than by ad-hoc heuristics to allow
43 mixed-type expressions to be meaningful, even with user-defined types.
44 </para>
45
46 <para>
47 The <productname>Postgres</productname> scanner/parser decodes lexical
48 elements into only five fundamental categories: integers, floats, strings,
49 names, and keywords.  Most extended types are first tokenized into
50 strings. The <acronym>SQL</acronym> language definition allows specifying type
51 names with strings, and this mechanism can be used in
52 <productname>Postgres</productname> to start the parser down the correct
53 path. For example, the query
54
55 <programlisting>
56 tgl=> SELECT text 'Origin' AS "Label", point '(0,0)' AS "Value";
57  Label  | Value
58 --------+-------
59  Origin | (0,0)
60 (1 row)
61 </programlisting>
62
63 has two strings, of type <type>text</type> and <type>point</type>.
64 If a type is not specified for a string, then the placeholder type
65 <firstterm>unknown</firstterm> is assigned initially, to be resolved in later
66 stages as described below.
67 </para>
68
69 <para>
70 There are four fundamental <acronym>SQL</acronym> constructs requiring
71 distinct type conversion rules in the <productname>Postgres</productname>
72 parser:
73 </para>
74
75 <variablelist>
76 <varlistentry>
77 <term>
78 Operators
79 </term>
80 <listitem>
81 <para>
82 <productname>Postgres</productname> allows expressions with
83 left- and right-unary (one argument) operators,
84 as well as binary (two argument) operators.
85 </para>
86 </listitem>
87 </varlistentry>
88 <varlistentry>
89 <term>
90 Function calls
91 </term>
92 <listitem>
93 <para>
94 Much of the <productname>Postgres</productname> type system is built around a
95 rich set of functions. Function calls have one or more arguments which, for
96 any specific query, must be matched to the functions available in the system
97 catalog.  Since <productname>Postgres</productname> permits function
98 overloading, the function name alone does not uniquely identify the function
99 to be called --- the parser must select the right function based on the data
100 types of the supplied arguments.
101 </para>
102 </listitem>
103 </varlistentry>
104 <varlistentry>
105 <term>
106 Query targets
107 </term>
108 <listitem>
109 <para>
110 <acronym>SQL</acronym> INSERT and UPDATE statements place the results of
111 expressions into a table. The expressions in the query must be matched up
112 with, and perhaps converted to, the types of the target columns.
113 </para>
114 </listitem>
115 </varlistentry>
116 <varlistentry>
117 <term>
118 UNION and CASE constructs
119 </term>
120 <listitem>
121 <para>
122 Since all select results from a UNION SELECT statement must appear in a single
123 set of columns, the types of the results
124 of each SELECT clause must be matched up and converted to a uniform set.
125 Similarly, the result expressions of a CASE construct must be coerced to
126 a common type so that the CASE expression as a whole has a known output type.
127 </para>
128 </listitem>
129 </varlistentry>
130 </variablelist>
131
132 <para>
133 Many of the general type conversion rules use simple conventions built on
134 the <productname>Postgres</productname> function and operator system tables.
135 There are some heuristics included in the conversion rules to better support
136 conventions for the <acronym>SQL92</acronym> standard native types such as
137 <type>smallint</type>, <type>integer</type>, and <type>float</type>.
138 </para>
139
140 <para>
141 The <productname>Postgres</productname> parser uses the convention that all
142 type conversion functions take a single argument of the source type and are
143 named with the same name as the target type. Any function meeting these
144 criteria is considered to be a valid conversion function, and may be used
145 by the parser as such. This simple assumption gives the parser the power
146 to explore type conversion possibilities without hardcoding, allowing
147 extended user-defined types to use these same features transparently.
148 </para>
149
150 <para>
151 An additional heuristic is provided in the parser to allow better guesses
152 at proper behavior for <acronym>SQL</acronym> standard types. There are
153 several basic <firstterm>type categories</firstterm> defined: <type>boolean</type>,
154 <type>numeric</type>, <type>string</type>, <type>bitstring</type>, <type>datetime</type>, <type>timespan</type>, <type>geometric</type>, <type>network</type>,
155 and user-defined. Each category, with the exception of user-defined, has
156 a <firstterm>preferred type</firstterm> which is preferentially selected
157 when there is ambiguity.
158 In the user-defined category, each type is its own preferred type.
159 Ambiguous expressions (those with multiple candidate parsing solutions)
160 can often be resolved when there are multiple possible built-in types, but
161 they will raise an error when there are multiple choices for user-defined
162 types.
163 </para>
164
165 <sect2>
166 <title>Guidelines</title>
167
168 <para>
169 All type conversion rules are designed with several principles in mind:
170
171 <itemizedlist mark="bullet" spacing="compact">
172 <listitem>
173 <para>
174 Implicit conversions should never have surprising or unpredictable outcomes.
175 </para>
176 </listitem>
177
178 <listitem>
179 <para>
180 User-defined types, of which the parser has no a-priori knowledge, should be
181 "higher" in the type hierarchy. In mixed-type expressions, native types shall always
182 be converted to a user-defined type (of course, only if conversion is necessary).
183 </para>
184 </listitem>
185
186 <listitem>
187 <para>
188 User-defined types are not related. Currently, <productname>Postgres</productname>
189 does not have information available to it on relationships between types, other than
190 hardcoded heuristics for built-in types and implicit relationships based on available functions
191 in the catalog.
192 </para>
193 </listitem>
194
195 <listitem>
196 <para>
197 There should be no extra overhead from the parser or executor
198 if a query does not need implicit type conversion.
199 That is, if a query is well formulated and the types already match up, then the query should proceed
200 without spending extra time in the parser and without introducing unnecessary implicit conversion
201 functions into the query.
202 </para>
203
204 <para>
205 Additionally, if a query usually requires an implicit conversion for a function, and
206 if then the user defines an explicit function with the correct argument types, the parser
207 should use this new function and will no longer do the implicit conversion using the old function.
208 </para>
209 </listitem>
210 </itemizedlist>
211 </para>
212 </sect2>
213 </sect1>
214
215 <sect1 id="typeconv-oper">
216 <title>Operators</title>
217
218 <procedure>
219 <title>Operator Type Resolution</title>
220
221 <step performance="required">
222 <para>
223 Check for an exact match in the pg_operator system catalog.
224 </para>
225
226 <substeps>
227 <step performance="optional">
228 <para>
229 If one argument of a binary operator is <type>unknown</type> type,
230 then assume it is the same type as the other argument for this check.
231 Other cases involving <type>unknown</type> will never find a match at
232 this step.
233 </para>
234 </step>
235 </substeps>
236 </step>
237
238 <step performance="required">
239 <para>
240 Look for the best match.
241 </para>
242 <substeps>
243 <step performance="required">
244 <para>
245 Make a list of all operators of the same name for which the input types
246 match or can be coerced to match.  (<type>unknown</type> literals are
247 assumed to be coercible to anything for this purpose.)  If there is only
248 one, use it; else continue to the next step.
249 </para>
250 </step>
251 <step performance="required">
252 <para>
253 Run through all candidates and keep those with the most exact matches
254 on input types.  Keep all candidates if none have any exact matches.
255 If only one candidate remains, use it; else continue to the next step.
256 </para>
257 </step>
258 <step performance="required">
259 <para>
260 Run through all candidates and keep those with the most exact or
261 binary-compatible matches on input types.  Keep all candidates if none have
262 any exact or binary-compatible matches.
263 If only one candidate remains, use it; else continue to the next step.
264 </para>
265 </step>
266 <step performance="required">
267 <para>
268 Run through all candidates and keep those that accept preferred types at
269 the most positions where type coercion will be required.
270 Keep all candidates if none accept preferred types.
271 If only one candidate remains, use it; else continue to the next step.
272 </para>
273 </step>
274 <step performance="required">
275 <para>
276 If any input arguments are <quote>unknown</quote>, check the type categories accepted
277 at those argument positions by the remaining candidates.  At each position,
278 select "string"
279 category if any candidate accepts that category (this bias towards string
280 is appropriate since an unknown-type literal does look like a string).
281 Otherwise, if all the remaining candidates accept the same type category,
282 select that category; otherwise fail because
283 the correct choice cannot be deduced without more clues.  Also note whether
284 any of the candidates accept a preferred data type within the selected category.
285 Now discard operator candidates that do not accept the selected type category;
286 furthermore, if any candidate accepts a preferred type at a given argument
287 position, discard candidates that accept non-preferred types for that
288 argument.
289 </para>
290 </step>
291 <step performance="required">
292 <para>
293 If only one candidate remains, use it.  If no candidate or more than one
294 candidate remains,
295 then fail.
296 </para>
297 </step>
298 </substeps>
299 </step>
300 </procedure>
301
302 <sect2>
303 <title>Examples</title>
304
305 <sect3>
306 <title>Exponentiation Operator</title>
307
308 <para>
309 There is only one exponentiation
310 operator defined in the catalog, and it takes arguments of type 
311 <type>double precision</type>.
312 The scanner assigns an initial type of <type>int4</type> to both arguments
313 of this query expression:
314 <programlisting>
315 tgl=> select 2 ^ 3 AS "Exp";
316  Exp
317 -----
318    8
319 (1 row)
320 </programlisting>
321
322 So the parser does a type conversion on both operands and the query
323 is equivalent to
324
325 <programlisting>
326 tgl=> select CAST(2 AS double precision) ^ CAST(3 AS double precision) AS "Exp";
327  Exp
328 -----
329    8
330 (1 row)
331 </programlisting>
332
333 or
334
335 <programlisting>
336 tgl=> select 2.0 ^ 3.0 AS "Exp";
337  Exp
338 -----
339    8
340 (1 row)
341 </programlisting>
342
343 <note>
344 <para>
345 This last form has the least overhead, since no functions are called to do
346 implicit type conversion. This is not an issue for small queries, but may
347 have an impact on the performance of queries involving large tables.
348 </para>
349 </note>
350 </para>
351 </sect3>
352
353 <sect3>
354 <title>String Concatenation</title>
355
356 <para>
357 A string-like syntax is used for working with string types as well as for
358 working with complex extended types.
359 Strings with unspecified type are matched with likely operator candidates.
360 </para>
361
362 <para>
363 One unspecified argument:
364 <programlisting>
365 tgl=> SELECT text 'abc' || 'def' AS "Text and Unknown";
366  Text and Unknown
367 ------------------
368  abcdef
369 (1 row)
370 </programlisting>
371 </para>
372
373 <para>
374 In this case the parser looks to see if there is an operator taking <type>text</type>
375 for both arguments. Since there is, it assumes that the second argument should
376 be interpreted as of type <type>text</type>.
377 </para>
378
379 <para>
380 Concatenation on unspecified types:
381 <programlisting>
382 tgl=> SELECT 'abc' || 'def' AS "Unspecified";
383  Unspecified
384 -------------
385  abcdef
386 (1 row)
387 </programlisting>
388 </para>
389
390 <para>
391 In this case there is no initial hint for which type to use, since no types
392 are specified in the query. So, the parser looks for all candidate operators
393 and finds that there are candidates accepting both string-category and
394 bit-string-category inputs.  Since string category is preferred when available,
395 that category is selected, and then the 
396 "preferred type" for strings, <type>text</type>, is used as the specific
397 type to resolve the unknown literals to.
398 </para>
399 </sect3>
400
401 <sect3>
402 <title>Factorial</title>
403
404 <para>
405 This example illustrates an interesting result. Traditionally, the
406 factorial operator is defined for integers only. The <productname>Postgres</productname>
407 operator catalog has only one entry for factorial, taking an integer operand.
408 If given a non-integer numeric argument, <productname>Postgres</productname>
409 will try to convert that argument to an integer for evaluation of the
410 factorial.
411
412 <programlisting>
413 tgl=> select (4.3 !);
414  ?column?
415 ----------
416        24
417 (1 row)
418 </programlisting>
419
420 <note>
421 <para>
422 Of course, this leads to a mathematically suspect result,
423 since in principle the factorial of a non-integer is not defined.
424 However, the role of a database is not to teach mathematics, but
425 to be a tool for data manipulation. If a user chooses to take the
426 factorial of a floating point number, <productname>Postgres</productname>
427 will try to oblige.
428 </para>
429 </note>
430 </para>
431 </sect3>
432 </sect2>
433 </sect1>
434
435 <sect1 id="typeconv-func">
436 <title>Functions</title>
437
438 <procedure>
439 <title>Function Call Type Resolution</title>
440
441 <step performance="required">
442 <para>
443 Check for an exact match in the <classname>pg_proc</classname> system catalog.
444 (Cases involving <type>unknown</type> will never find a match at
445 this step.)
446 </para></step>
447 <step performance="required">
448 <para>
449 Look for the best match.
450 </para>
451 <substeps>
452 <step performance="required">
453 <para>
454 Make a list of all functions of the same name with the same number of
455 arguments for which the input types
456 match or can be coerced to match.  (<type>unknown</type> literals are
457 assumed to be coercible to anything for this purpose.)  If there is only
458 one, use it; else continue to the next step.
459 </para>
460 </step>
461 <step performance="required">
462 <para>
463 Run through all candidates and keep those with the most exact matches
464 on input types.  Keep all candidates if none have any exact matches.
465 If only one candidate remains, use it; else continue to the next step.
466 </para>
467 </step>
468 <step performance="required">
469 <para>
470 Run through all candidates and keep those with the most exact or
471 binary-compatible matches on input types.  Keep all candidates if none have
472 any exact or binary-compatible matches.
473 If only one candidate remains, use it; else continue to the next step.
474 </para>
475 </step>
476 <step performance="required">
477 <para>
478 Run through all candidates and keep those that accept preferred types at
479 the most positions where type coercion will be required.
480 Keep all candidates if none accept preferred types.
481 If only one candidate remains, use it; else continue to the next step.
482 </para>
483 </step>
484 <step performance="required">
485 <para>
486 If any input arguments are "unknown", check the type categories accepted
487 at those argument positions by the remaining candidates.  At each position,
488 select "string"
489 category if any candidate accepts that category (this bias towards string
490 is appropriate since an unknown-type literal does look like a string).
491 Otherwise, if all the remaining candidates accept the same type category,
492 select that category; otherwise fail because
493 the correct choice cannot be deduced without more clues.  Also note whether
494 any of the candidates accept a preferred data type within the selected category.
495 Now discard operator candidates that do not accept the selected type category;
496 furthermore, if any candidate accepts a preferred type at a given argument
497 position, discard candidates that accept non-preferred types for that
498 argument.
499 </para>
500 </step>
501 <step performance="required">
502 <para>
503 If only one candidate remains, use it.  If no candidate or more than one
504 candidate remains,
505 then fail.
506 </para>
507 </step>
508 </substeps>
509 </step>
510 <step performance="required">
511 <para>
512 If no best match could be identified, see whether the function call appears
513 to be a trivial type coercion request.  This happens if the function call
514 has just one argument and the function name is the same as the (internal)
515 name of some data type.  Furthermore, the function argument must be either
516 an unknown-type literal or a type that is binary-compatible with the named
517 data type.  When these conditions are met, the function argument is coerced
518 to the named data type.
519 </para>
520 </step>
521 </procedure>
522
523 <sect2>
524 <title>Examples</title>
525
526 <sect3>
527 <title>Factorial Function</title>
528
529 <para>
530 There is only one factorial function defined in the <classname>pg_proc</classname> catalog.
531 So the following query automatically converts the <type>int2</type> argument
532 to <type>int4</type>:
533
534 <programlisting>
535 tgl=> select int4fac(int2 '4');
536  int4fac
537 ---------
538       24
539 (1 row)
540 </programlisting>
541
542 and is actually transformed by the parser to
543 <programlisting>
544 tgl=> select int4fac(int4(int2 '4'));
545  int4fac
546 ---------
547       24
548 (1 row)
549 </programlisting>
550 </para>
551 </sect3>
552
553 <sect3>
554 <title>Substring Function</title>
555
556 <para>
557 There are two <function>substr</function> functions declared in <classname>pg_proc</classname>. However,
558 only one takes two arguments, of types <type>text</type> and <type>int4</type>.
559 </para>
560
561 <para>
562 If called with a string constant of unspecified type, the type is matched up
563 directly with the only candidate function type:
564 <programlisting>
565 tgl=> select substr('1234', 3);
566  substr
567 --------
568      34
569 (1 row)
570 </programlisting>
571 </para>
572
573 <para>
574 If the string is declared to be of type <type>varchar</type>, as might be the case
575 if it comes from a table, then the parser will try to coerce it to become <type>text</type>:
576 <programlisting>
577 tgl=> select substr(varchar '1234', 3);
578  substr
579 --------
580      34
581 (1 row)
582 </programlisting>
583 which is transformed by the parser to become
584 <programlisting>
585 tgl=> select substr(text(varchar '1234'), 3);
586  substr
587 --------
588      34
589 (1 row)
590 </programlisting>
591 </para>
592 <note>
593 <para>
594 Actually, the parser is aware that <type>text</type> and <type>varchar</type>
595 are "binary compatible", meaning that one can be passed to a function that
596 accepts the other without doing any physical conversion.  Therefore, no
597 explicit type conversion call is really inserted in this case.
598 </para>
599 </note>
600
601 <para>
602 And, if the function is called with an <type>int4</type>, the parser will
603 try to convert that to <type>text</type>:
604 <programlisting>
605 tgl=> select substr(1234, 3);
606  substr
607 --------
608      34
609 (1 row)
610 </programlisting>
611 actually executes as
612 <programlisting>
613 tgl=> select substr(text(1234), 3);
614  substr
615 --------
616      34
617 (1 row)
618 </programlisting>
619 This succeeds because there is a conversion function text(int4) in the
620 system catalog.
621 </para>
622 </sect3>
623 </sect2>
624 </sect1>
625
626 <sect1 id="typeconv-query">
627 <title>Query Targets</title>
628
629 <procedure>
630 <title>Query Target Type Resolution</title>
631
632 <step performance="required">
633 <para>
634 Check for an exact match with the target.
635 </para></step>
636 <step performance="required">
637 <para>
638 Otherwise, try to coerce the expression to the target type.  This will succeed
639 if the two types are known binary-compatible, or if there is a conversion
640 function.  If the expression is an unknown-type literal, the contents of
641 the literal string will be fed to the input conversion routine for the target
642 type.
643 </para></step>
644
645 <step performance="required">
646 <para>
647 If the target is a fixed-length type (e.g. <type>char</type> or <type>varchar</type>
648 declared with a length) then try to find a sizing function for the target
649 type.  A sizing function is a function of the same name as the type,
650 taking two arguments of which the first is that type and the second is an
651 integer, and returning the same type.  If one is found, it is applied,
652 passing the column's declared length as the second parameter.
653 </para></step>
654
655 </procedure>
656
657 <sect2>
658 <title>Examples</title>
659
660 <sect3>
661 <title><type>varchar</type> Storage</title>
662
663 <para>
664 For a target column declared as <type>varchar(4)</type> the following query
665 ensures that the target is sized correctly:
666
667 <programlisting>
668 tgl=> CREATE TABLE vv (v varchar(4));
669 CREATE
670 tgl=> INSERT INTO vv SELECT 'abc' || 'def';
671 INSERT 392905 1
672 tgl=> SELECT * FROM vv;
673   v
674 ------
675  abcd
676 (1 row)
677 </programlisting>
678
679 What's really happened here is that the two unknown literals are resolved
680 to text by default, allowing the <literal>||</literal> operator to be
681 resolved as text concatenation.  Then the text result of the operator
682 is coerced to <type>varchar</type> to match the target column type.  (But, since the
683 parser knows that text and <type>varchar</type> are binary-compatible, this coercion
684 is implicit and does not insert any real function call.)  Finally, the
685 sizing function <literal>varchar(varchar,int4)</literal> is found in the system
686 catalogs and applied to the operator's result and the stored column length.
687 This type-specific function performs the desired truncation.
688 </para>
689 </sect3>
690 </sect2>
691 </sect1>
692
693 <sect1 id="typeconv-union-case">
694 <title>UNION and CASE Constructs</title>
695
696 <para>
697 The UNION and CASE constructs must match up possibly dissimilar types to
698 become a single result set.  The resolution algorithm is applied separately to
699 each output column of a UNION.  CASE uses the identical algorithm to match
700 up its result expressions.
701 </para>
702 <procedure>
703 <title>UNION and CASE Type Resolution</title>
704
705 <step performance="required">
706 <para>
707 If all inputs are of type <type>unknown</type>, resolve as type
708 <type>text</type> (the preferred type for string category).
709 Otherwise, ignore the <type>unknown</type> inputs while choosing the type.
710 </para></step>
711
712 <step performance="required">
713 <para>
714 If the non-unknown inputs are not all of the same type category, fail.
715 </para></step>
716
717 <step performance="required">
718 <para>
719 If one or more non-unknown inputs are of a preferred type in that category,
720 resolve as that type.
721 </para></step>
722
723 <step performance="required">
724 <para>
725 Otherwise, resolve as the type of the first non-unknown input.
726 </para></step>
727
728 <step performance="required">
729 <para>
730 Coerce all inputs to the selected type.
731 </para></step>
732 </procedure>
733
734 <sect2>
735 <title>Examples</title>
736
737 <sect3>
738 <title>Underspecified Types</title>
739
740 <para>
741 <programlisting>
742 tgl=> SELECT text 'a' AS "Text" UNION SELECT 'b';
743  Text
744 ------
745  a
746  b
747 (2 rows)
748 </programlisting>
749 Here, the unknown-type literal 'b' will be resolved as type text.
750 </para>
751 </sect3>
752
753 <sect3>
754 <title>Simple UNION</title>
755
756 <para>
757 <programlisting>
758 tgl=> SELECT 1.2 AS "Double" UNION SELECT 1;
759  Double
760 --------
761       1
762     1.2
763 (2 rows)
764 </programlisting>
765 </para>
766 </sect3>
767
768 <sect3>
769 <title>Transposed UNION</title>
770
771 <para>
772 Here the output type of the union is forced to match the type of
773 the first/top clause in the union:
774
775 <programlisting>
776 tgl=> SELECT 1 AS "All integers"
777 tgl-> UNION SELECT CAST('2.2' AS REAL);
778  All integers
779 --------------
780             1
781             2
782 (2 rows)
783 </programlisting>
784 </para>
785 <para>
786 Since <type>REAL</type> is not a preferred type, the parser sees no reason
787 to select it over <type>INTEGER</type> (which is what the 1 is), and instead
788 falls back on the use-the-first-alternative rule.
789 This example demonstrates that the preferred-type mechanism doesn't encode
790 as much information as we'd like.  Future versions of
791 <productname>Postgres</productname> may support a more general notion of
792 type preferences.
793 </para>
794 </sect3>
795 </sect2>
796 </sect1>
797 </chapter>
798
799 <!-- Keep this comment at the end of the file
800 Local variables:
801 mode:sgml
802 sgml-omittag:t
803 sgml-shorttag:t
804 sgml-minimize-attributes:nil
805 sgml-always-quote-attributes:t
806 sgml-indent-step:1
807 sgml-indent-data:t
808 sgml-parent-document:nil
809 sgml-default-dtd-file:"./reference.ced"
810 sgml-exposed-tags:nil
811 sgml-local-catalogs:("/usr/lib/sgml/catalog")
812 sgml-local-ecat-files:nil
813 End:
814 -->