? dq.diff ? dq2.diff ? dq3.diff Index: doc/src/sgml/plperl.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/plperl.sgml,v retrieving revision 2.22 diff -2 -c -r2.22 plperl.sgml *** doc/src/sgml/plperl.sgml 14 Dec 2003 00:10:32 -0000 2.22 --- doc/src/sgml/plperl.sgml 14 May 2004 17:05:07 -0000 *************** *** 47,53 **** To create a function in the PL/Perl language, use the standard syntax: ! CREATE FUNCTION funcname (argument-types) RETURNS return-type AS ' # PL/Perl function body ! ' LANGUAGE plperl; The body of the function is ordinary Perl code. --- 47,54 ---- To create a function in the PL/Perl language, use the standard syntax: ! CREATE FUNCTION funcname ! (argument-types) RETURNS return-type AS $$ # PL/Perl function body ! $$ LANGUAGE plperl; The body of the function is ordinary Perl code. *************** *** 66,73 **** ! CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS ' if ($_[0] > $_[1]) { return $_[0]; } return $_[1]; ! ' LANGUAGE plperl; --- 67,74 ---- ! CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$ if ($_[0] > $_[1]) { return $_[0]; } return $_[1]; ! $$ LANGUAGE plperl; *************** *** 89,93 **** ! CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS ' my ($a,$b) = @_; if (! defined $a) { --- 90,94 ---- ! CREATE FUNCTION perl_max (integer, integer) RETURNS integer AS $$ my ($a,$b) = @_; if (! defined $a) { *************** *** 98,102 **** if ($a > $b) { return $a; } return $b; ! ' LANGUAGE plperl; --- 99,103 ---- if ($a > $b) { return $a; } return $b; ! $$ LANGUAGE plperl; *************** *** 120,127 **** ); ! CREATE FUNCTION empcomp(employee) RETURNS integer AS ' my ($emp) = @_; ! return $emp->{''basesalary''} + $emp->{''bonus''}; ! ' LANGUAGE plperl; SELECT name, empcomp(employee) FROM employee; --- 121,128 ---- ); ! CREATE FUNCTION empcomp(employee) RETURNS integer AS $$ my ($emp) = @_; ! return $emp->{'basesalary'} + $emp->{'bonus'}; ! $$ LANGUAGE plperl; SELECT name, empcomp(employee) FROM employee; *************** *** 137,146 **** Because the function body is passed as an SQL string literal to ! CREATE FUNCTION, you have to escape single ! quotes and backslashes within your Perl source, typically by ! doubling them as shown in the above example. Another possible ! approach is to avoid writing single quotes by using Perl's ! extended quoting operators (q[], ! qq[], qw[]). --- 138,147 ---- Because the function body is passed as an SQL string literal to ! CREATE FUNCTION, you have to use dollar quoting ! or escape single quotes and backslashes within your Perl source, ! typically by doubling them. Another possible approach is to avoid ! writing single quotes by using Perl's extended quoting operators ! (q[], qq[], ! qw[]). *************** *** 227,235 **** system operations are not allowed for security reasons: ! CREATE FUNCTION badfunc() RETURNS integer AS ' open(TEMP, ">/tmp/badfile"); print TEMP "Gotcha!\n"; return 1; ! ' LANGUAGE plperl; The creation of the function will succeed, but executing it will not. --- 228,236 ---- system operations are not allowed for security reasons: ! CREATE FUNCTION badfunc() RETURNS integer AS $$ open(TEMP, ">/tmp/badfile"); print TEMP "Gotcha!\n"; return 1; ! $$ LANGUAGE plperl; The creation of the function will succeed, but executing it will not. Index: doc/src/sgml/plpgsql.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/plpgsql.sgml,v retrieving revision 1.37 diff -2 -c -r1.37 plpgsql.sgml *** doc/src/sgml/plpgsql.sgml 26 Mar 2004 03:18:28 -0000 1.37 --- doc/src/sgml/plpgsql.sgml 14 May 2004 17:05:08 -0000 *************** *** 90,94 **** ! CREATE FUNCTION populate() RETURNS integer AS ' DECLARE -- declarations --- 90,94 ---- ! CREATE FUNCTION populate() RETURNS integer AS $$ DECLARE -- declarations *************** *** 96,100 **** PERFORM my_function(); END; ! ' LANGUAGE plpgsql; --- 96,100 ---- PERFORM my_function(); END; ! $$ LANGUAGE plpgsql; *************** *** 262,272 **** Since the code of a PL/pgSQL function is specified in CREATE FUNCTION as a string literal, single ! quotes inside the function body must be escaped by doubling them. ! This can lead to ! rather complicated code at times, especially if you are writing a ! function that generates other functions, as in the example in . This chart may be useful ! as a summary of the needed numbers of quotation marks in ! various situations. --- 262,276 ---- Since the code of a PL/pgSQL function is specified in CREATE FUNCTION as a string literal, single ! quotes inside the function body must be escaped by doubling them ! unless the string literal comprising the function body is dollar ! quoted. ! ! ! ! Doubling can lead to incomprehensible code at times, especially if ! you are writing a function that generates other functions, as in the ! example in . This ! chart may be useful when translating pre-dollar quoting code into ! something that is comprehensible. *************** *** 419,427 **** block is entered, not only once per function call. For example: ! CREATE FUNCTION somefunc() RETURNS integer AS ' DECLARE quantity integer := 30; BEGIN ! RAISE NOTICE ''Quantity here is %'', quantity; -- Quantity here is 30 quantity := 50; -- --- 423,431 ---- block is entered, not only once per function call. For example: ! CREATE FUNCTION somefunc() RETURNS integer AS $$ DECLARE quantity integer := 30; BEGIN ! RAISE NOTICE 'Quantity here is %', quantity; -- Quantity here is 30 quantity := 50; -- *************** *** 431,442 **** quantity integer := 80; BEGIN ! RAISE NOTICE ''Quantity here is %'', quantity; -- Quantity here is 80 END; ! RAISE NOTICE ''Quantity here is %'', quantity; -- Quantity here is 50 RETURN quantity; END; ! ' LANGUAGE plpgsql; --- 435,446 ---- quantity integer := 80; BEGIN ! RAISE NOTICE 'Quantity here is %', quantity; -- Quantity here is 80 END; ! RAISE NOTICE 'Quantity here is %', quantity; -- Quantity here is 50 RETURN quantity; END; ! $$ LANGUAGE plpgsql; *************** *** 449,453 **** are always executed within a transaction established by an outer query --- they cannot start or commit transactions, since ! PostgreSQL does not have nested transactions. --- 453,457 ---- are always executed within a transaction established by an outer query --- they cannot start or commit transactions, since ! PostgreSQL does not yet have nested transactions. *************** *** 511,515 **** quantity integer DEFAULT 32; ! url varchar := ''http://mysite.com''; user_id CONSTANT integer := 10; --- 515,519 ---- quantity integer DEFAULT 32; ! url varchar := 'http://mysite.com'; user_id CONSTANT integer := 10; *************** *** 532,536 **** Some examples: ! CREATE FUNCTION sales_tax(real) RETURNS real AS ' DECLARE subtotal ALIAS FOR $1; --- 536,540 ---- Some examples: ! CREATE FUNCTION sales_tax(real) RETURNS real AS $$ DECLARE subtotal ALIAS FOR $1; *************** *** 538,545 **** RETURN subtotal * 0.06; END; ! ' LANGUAGE plpgsql; ! CREATE FUNCTION instr(varchar, integer) RETURNS integer AS ' DECLARE v_string ALIAS FOR $1; --- 542,549 ---- RETURN subtotal * 0.06; END; ! $$ LANGUAGE plpgsql; ! CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$ DECLARE v_string ALIAS FOR $1; *************** *** 548,555 **** -- some computations here END; ! ' LANGUAGE plpgsql; ! CREATE FUNCTION concat_selected_fields(tablename) RETURNS text AS ' DECLARE in_t ALIAS FOR $1; --- 552,559 ---- -- some computations here END; ! $$ LANGUAGE plpgsql; ! CREATE FUNCTION concat_selected_fields(tablename) RETURNS text AS $$ DECLARE in_t ALIAS FOR $1; *************** *** 557,561 **** RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7; END; ! ' LANGUAGE plpgsql; --- 561,565 ---- RETURN in_t.f1 || in_t.f3 || in_t.f5 || in_t.f7; END; ! $$ LANGUAGE plpgsql; *************** *** 577,581 **** CREATE FUNCTION add_three_values(anyelement, anyelement, anyelement) ! RETURNS anyelement AS ' DECLARE result ALIAS FOR $0; --- 581,585 ---- CREATE FUNCTION add_three_values(anyelement, anyelement, anyelement) ! RETURNS anyelement AS $$ DECLARE result ALIAS FOR $0; *************** *** 587,591 **** RETURN result; END; ! ' LANGUAGE plpgsql; --- 591,595 ---- RETURN result; END; ! $$ LANGUAGE plpgsql; *************** *** 678,682 **** Here is an example of using composite types: ! CREATE FUNCTION use_two_tables(tablename) RETURNS text AS ' DECLARE in_t ALIAS FOR $1; --- 682,686 ---- Here is an example of using composite types: ! CREATE FUNCTION use_two_tables(tablename) RETURNS text AS $$ DECLARE in_t ALIAS FOR $1; *************** *** 686,690 **** RETURN in_t.f1 || use_t.f3 || in_t.f5 || use_t.f7; END; ! ' LANGUAGE plpgsql; SELECT use_two_tables(t.*) FROM tablename t WHERE ... ; --- 690,694 ---- RETURN in_t.f1 || use_t.f3 || in_t.f5 || use_t.f7; END; ! $$ LANGUAGE plpgsql; SELECT use_two_tables(t.*) FROM tablename t WHERE ... ; *************** *** 789,800 **** ! CREATE FUNCTION logfunc1(text) RETURNS timestamp AS ' DECLARE logtxt ALIAS FOR $1; BEGIN ! INSERT INTO logtable VALUES (logtxt, ''now''); ! RETURN ''now''; END; ! ' LANGUAGE plpgsql; --- 793,804 ---- ! CREATE FUNCTION logfunc1(text) RETURNS timestamp AS $$ DECLARE logtxt ALIAS FOR $1; BEGIN ! INSERT INTO logtable VALUES (logtxt, 'now'); ! RETURN 'now'; END; ! $$ LANGUAGE plpgsql; *************** *** 802,815 **** ! CREATE FUNCTION logfunc2(text) RETURNS timestamp AS ' DECLARE logtxt ALIAS FOR $1; curtime timestamp; BEGIN ! curtime := ''now''; INSERT INTO logtable VALUES (logtxt, curtime); RETURN curtime; END; ! ' LANGUAGE plpgsql; --- 806,819 ---- ! CREATE FUNCTION logfunc2(text) RETURNS timestamp AS $$ DECLARE logtxt ALIAS FOR $1; curtime timestamp; BEGIN ! curtime := 'now'; INSERT INTO logtable VALUES (logtxt, curtime); RETURN curtime; END; ! $$ LANGUAGE plpgsql; *************** *** 871,875 **** listed here. ! Assignment --- 875,879 ---- listed here. ! Assignment *************** *** 969,977 **** INTO statement to determine whether the assignment was successful (that is, at least one row was was returned by the query). For example: ! SELECT INTO myrec * FROM emp WHERE empname = myname; IF NOT FOUND THEN ! RAISE EXCEPTION ''employee % not found'', myname; END IF; --- 973,981 ---- INTO statement to determine whether the assignment was successful (that is, at least one row was was returned by the query). For example: ! SELECT INTO myrec * FROM emp WHERE empname = myname; IF NOT FOUND THEN ! RAISE EXCEPTION 'employee % not found', myname; END IF; *************** *** 979,982 **** --- 983,991 ---- + You were using dollar quoting to make the + function body, weren't you? + + + To test for whether a record/row result is null, you can use the IS NULL conditional. There is, however, no *************** *** 992,996 **** IF users_rec.homepage IS NULL THEN -- user entered no homepage, return "http://" ! RETURN ''http://''; END IF; END; --- 1001,1005 ---- IF users_rec.homepage IS NULL THEN -- user entered no homepage, return "http://" ! RETURN 'http://'; END IF; END; *************** *** 1033,1044 **** An example: ! PERFORM create_mv(''cs_session_page_requests_mv'', my_query); ! Executing Dynamic Commands ! Oftentimes you will want to generate dynamic commands inside your --- 1042,1053 ---- An example: ! PERFORM create_mv('cs_session_page_requests_mv', my_query); ! Executing Dynamic Commands ! Oftentimes you will want to generate dynamic commands inside your *************** *** 1067,1076 **** ! When working with dynamic commands you will have to face ! escaping of single quotes in PL/pgSQL. Please refer to the ! overview in , ! which can save you some effort. ! Unlike all other commands in PL/pgSQL, a command --- 1076,1087 ---- ! When working with dynamic commands you will have to face escaping ! of single quotes in PL/pgSQL. The recommended method ! is dollar quoting. If you have legacy code which does ! not use dollar quoting, please refer to the ! overview in , which can save you ! some effort when translating said code to a more reasonable scheme. ! Unlike all other commands in PL/pgSQL, a command *************** *** 1081,1085 **** actions on variable tables and columns. ! The results from SELECT commands are discarded --- 1092,1096 ---- actions on variable tables and columns. ! The results from SELECT commands are discarded *************** *** 1094,1104 **** ! An example: ! EXECUTE ''UPDATE tbl SET '' || quote_ident(colname) ! || '' = '' || quote_literal(newvalue) ! || '' WHERE ...''; --- 1105,1117 ---- ! An example (except where noted, all examples herein assume that ! you have dollar quoting and are using it): ! ! EXECUTE 'UPDATE tbl SET ' || quote_ident(colname) ! || ' = ' || quote_literal(newvalue) ! || ' WHERE ...'; *************** *** 1145,1155 **** || referrer_keys.referrer_type || ''''''; END IF;''; END LOOP; ! a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE plpgsql;''; ! EXECUTE a_output; END; ' LANGUAGE plpgsql; --- 1158,1202 ---- || referrer_keys.referrer_type || ''''''; END IF;''; END LOOP; ! a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE plpgsql;''; ! EXECUTE a_output; END; ' LANGUAGE plpgsql; + + And here is an equivalent using dollar quoting. At least it is more + legible than the above, although both versions show that the design, + rather than merely the formatting, needs to be re-thought entire. + + + CREATE or replace FUNCTION cs_update_referrer_type_proc2() RETURNS integer AS $func$ + DECLARE + referrer_keys RECORD; -- declare a generic record to be used in a FOR + a_output varchar(4000); + BEGIN + a_output := 'CREATE FUNCTION cs_find_referrer_type(varchar, varchar, varchar) + RETURNS varchar AS $innerfunc$ + DECLARE + v_host ALIAS FOR $1; + v_domain ALIAS FOR $2; + v_url ALIAS FOR $3; + BEGIN '; + + -- Notice how we scan through the results of a query in a FOR loop + -- using the FOR <record> construct. + + FOR referrer_keys IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP + a_output := a_output || ' IF v_' || referrer_keys.kind || ' LIKE $$' + || referrer_keys.key_string || '$$ THEN RETURN $$' + || referrer_keys.referrer_type || '$$; END IF;'; + END LOOP; + + a_output := a_output || ' RETURN NULL; END; $innerfunc$ LANGUAGE plpgsql;'; + EXECUTE a_output; + RETURN + END; + $func$ LANGUAGE plpgsql; + *************** *** 1253,1257 **** flexible and powerful way. ! Returning From a Function --- 1300,1304 ---- flexible and powerful way. ! Returning From a Function *************** *** 1363,1367 **** ! Conditionals --- 1410,1414 ---- ! Conditionals *************** *** 1435,1443 **** Examples: ! IF parentid IS NULL OR parentid = '''' THEN RETURN fullname; ELSE ! RETURN hp_true_filename(parentid) || ''/'' || fullname; END IF; --- 1482,1490 ---- Examples: ! IF parentid IS NULL OR parentid = '' THEN RETURN fullname; ELSE ! RETURN hp_true_filename(parentid) || '/' || fullname; END IF; *************** *** 1446,1452 **** IF v_count > 0 THEN INSERT INTO users_count (count) VALUES (v_count); ! RETURN ''t''; ELSE ! RETURN ''f''; END IF; --- 1493,1499 ---- IF v_count > 0 THEN INSERT INTO users_count (count) VALUES (v_count); ! RETURN 't'; ELSE ! RETURN 'f'; END IF; *************** *** 1462,1470 **** ! IF demo_row.sex = ''m'' THEN ! pretty_sex := ''man''; ELSE ! IF demo_row.sex = ''f'' THEN ! pretty_sex := ''woman''; END IF; END IF; --- 1509,1517 ---- ! IF demo_row.sex = 'm' THEN ! pretty_sex := 'man'; ELSE ! IF demo_row.sex = 'f' THEN ! pretty_sex := 'woman'; END IF; END IF; *************** *** 1515,1526 **** IF number = 0 THEN ! result := ''zero''; ELSIF number > 0 THEN ! result := ''positive''; ELSIF number < 0 THEN ! result := ''negative''; ELSE -- hmm, the only other possibility is that number is null ! result := ''NULL''; END IF; --- 1562,1573 ---- IF number = 0 THEN ! result := 'zero'; ELSIF number > 0 THEN ! result := 'positive'; ELSIF number < 0 THEN ! result := 'negative'; ELSE -- hmm, the only other possibility is that number is null ! result := 'NULL'; END IF; *************** *** 1667,1671 **** FOR i IN 1..10 LOOP -- some computations here ! RAISE NOTICE ''i is %'', i; END LOOP; --- 1714,1718 ---- FOR i IN 1..10 LOOP -- some computations here ! RAISE NOTICE 'i is %', i; END LOOP; *************** *** 1705,1709 **** mviews RECORD; BEGIN ! PERFORM cs_log(''Refreshing materialized views...''); FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP --- 1752,1756 ---- mviews RECORD; BEGIN ! PERFORM cs_log('Refreshing materialized views...'); FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP *************** *** 1711,1720 **** -- Now "mviews" has one record from cs_materialized_views ! PERFORM cs_log(''Refreshing materialized view '' || quote_ident(mviews.mv_name) || ''...''); ! EXECUTE ''TRUNCATE TABLE '' || quote_ident(mviews.mv_name); ! EXECUTE ''INSERT INTO '' || quote_ident(mviews.mv_name) || '' '' || mviews.mv_query; END LOOP; ! PERFORM cs_log(''Done refreshing materialized views.''); RETURN 1; END; --- 1758,1767 ---- -- Now "mviews" has one record from cs_materialized_views ! PERFORM cs_log('Refreshing materialized view ' || quote_ident(mviews.mv_name) || '...'); ! EXECUTE 'TRUNCATE TABLE ' || quote_ident(mviews.mv_name); ! EXECUTE 'INSERT INTO ' || quote_ident(mviews.mv_name) || ' ' || mviews.mv_query; END LOOP; ! PERFORM cs_log('Done refreshing materialized views.'); RETURN 1; END; *************** *** 1779,1783 **** large row sets from functions. ! Declaring Cursor Variables --- 1826,1830 ---- large row sets from functions. ! Declaring Cursor Variables *************** *** 1878,1882 **** An example: ! OPEN curs1 FOR EXECUTE ''SELECT * FROM '' || quote_ident($1); --- 1925,1929 ---- An example: ! OPEN curs1 FOR EXECUTE 'SELECT * FROM ' || quote_ident($1); *************** *** 1979,1983 **** ! Returning Cursors --- 2026,2030 ---- ! Returning Cursors *************** *** 2041,2045 **** The following example uses automatic cursor name generation: ! CREATE FUNCTION reffunc2() RETURNS refcursor AS ' --- 2088,2092 ---- The following example uses automatic cursor name generation: ! CREATE FUNCTION reffunc2() RETURNS refcursor AS ' *************** *** 2054,2058 **** BEGIN; SELECT reffunc2(); ! reffunc2 -------------------- --- 2101,2105 ---- BEGIN; SELECT reffunc2(); ! reffunc2 -------------------- *************** *** 2104,2108 **** --- 2151,2155 ---- *************** *** 2112,2116 **** % in the string: ! RAISE NOTICE ''Calling cs_create_job(%)'', v_job_id; --- 2159,2163 ---- % in the string: ! RAISE NOTICE 'Calling cs_create_job(%)', v_job_id; *************** *** 2119,2123 **** This example will abort the transaction with the given error message: ! RAISE EXCEPTION ''Inexistent ID --> %'', user_id; --- 2166,2170 ---- This example will abort the transaction with the given error message: ! RAISE EXCEPTION 'Inexistent ID --> %', user_id; *************** *** 2172,2176 **** trigger, several special variables are created automatically in the top-level block. They are: ! --- 2219,2223 ---- trigger, several special variables are created automatically in the top-level block. They are: ! *************** *** 2335,2359 **** ); ! CREATE FUNCTION emp_stamp() RETURNS trigger AS ' BEGIN -- Check that empname and salary are given IF NEW.empname IS NULL THEN ! RAISE EXCEPTION ''empname cannot be null''; END IF; IF NEW.salary IS NULL THEN ! RAISE EXCEPTION ''% cannot have null salary'', NEW.empname; END IF; -- Who works for us when she must pay for it? IF NEW.salary < 0 THEN ! RAISE EXCEPTION ''% cannot have a negative salary'', NEW.empname; END IF; -- Remember who changed the payroll when ! NEW.last_date := ''now''; NEW.last_user := current_user; RETURN NEW; END; ! ' LANGUAGE plpgsql; CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp --- 2382,2406 ---- ); ! CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$ BEGIN -- Check that empname and salary are given IF NEW.empname IS NULL THEN ! RAISE EXCEPTION 'empname cannot be null'; END IF; IF NEW.salary IS NULL THEN ! RAISE EXCEPTION '% cannot have null salary', NEW.empname; END IF; -- Who works for us when she must pay for it? IF NEW.salary < 0 THEN ! RAISE EXCEPTION '% cannot have a negative salary', NEW.empname; END IF; -- Remember who changed the payroll when ! NEW.last_date := 'now'; NEW.last_user := current_user; RETURN NEW; END; ! $emp_stamp$ LANGUAGE plpgsql; CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp *************** *** 2452,2456 **** RETURN v_name; END IF; ! RETURN v_name || '/' || v_version; END; / --- 2499,2503 ---- RETURN v_name; END IF; ! RETURN v_name || ''/'' || v_version; END; / *************** *** 2515,2519 **** CREATE OR REPLACE FUNCTION cs_fmt_browser_version(varchar, varchar) ! RETURNS varchar AS ' DECLARE v_name ALIAS FOR $1; --- 2562,2566 ---- CREATE OR REPLACE FUNCTION cs_fmt_browser_version(varchar, varchar) ! RETURNS varchar AS $$ DECLARE v_name ALIAS FOR $1; *************** *** 2523,2529 **** return v_name; END IF; ! RETURN v_name || ''/'' || v_version; END; ! ' LANGUAGE plpgsql; --- 2570,2576 ---- return v_name; END IF; ! RETURN v_name || '/' || v_version; END; ! $$ LANGUAGE plpgsql; *************** *** 2535,2539 **** ensuing quoting problems. ! Porting a Function that Creates Another Function from <application>PL/SQL</> to <application>PL/pgSQL</> --- 2582,2586 ---- ensuing quoting problems. ! Porting a Function that Creates Another Function from <application>PL/SQL</> to <application>PL/pgSQL</> *************** *** 2578,2615 **** ! CREATE FUNCTION cs_update_referrer_type_proc() RETURNS integer AS ' DECLARE ! referrer_keys RECORD; -- Declare a generic record to be used in a FOR ! a_output varchar(4000); BEGIN ! a_output := ''CREATE FUNCTION cs_find_referrer_type(varchar, varchar, varchar) ! RETURNS varchar AS '''' DECLARE v_host ALIAS FOR $1; v_domain ALIAS FOR $2; v_url ALIAS FOR $3; ! BEGIN ''; ! -- Notice how we scan through the results of a query in a FOR loop ! -- using the FOR <record> construct. ! FOR referrer_keys IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP ! a_output := a_output || '' IF v_'' || referrer_keys.kind || '' LIKE '''''''''' ! || referrer_keys.key_string || '''''''''' THEN RETURN '''''' ! || referrer_keys.referrer_type || ''''''; END IF;''; ! END LOOP; ! ! a_output := a_output || '' RETURN NULL; END; '''' LANGUAGE plpgsql;''; ! ! -- EXECUTE will work because we are not substituting any variables. ! -- Otherwise it would fail. Look at PERFORM for another way to run functions. ! ! EXECUTE a_output; END; ! ' LANGUAGE plpgsql; ! shows how to port a function --- 2625,2660 ---- ! CREATE or replace FUNCTION cs_update_referrer_type_proc2() RETURNS ! text AS $func$ DECLARE ! referrer_keys RECORD; -- declare a generic record to be used in a FOR ! a_output TEXT; BEGIN ! a_output := 'CREATE FUNCTION cs_find_referrer_type(varchar, varchar, varchar) ! RETURNS varchar AS $innerfunc$ DECLARE v_host ALIAS FOR $1; v_domain ALIAS FOR $2; v_url ALIAS FOR $3; ! BEGIN '; ! -- Notice how we scan through the results of a query in a FOR loop ! -- using the FOR <record> construct. ! FOR referrer_keys IN SELECT * FROM cs_referrer_keys ORDER BY try_order LOOP ! a_output := a_output || ' IF v_' || referrer_keys.kind || ' LIKE $$' ! || referrer_keys.key_string || '$$ THEN RETURN $$' ! || referrer_keys.referrer_type || '$$; END IF;'; ! END LOOP; ! ! a_output := a_output || ' RETURN NULL; END; $innerfunc$ LANGUAGE plpgsql;'; ! ! return a_output; END; ! $func$ LANGUAGE plpgsql; ! shows how to port a function *************** *** 2687,2691 **** ! CREATE OR REPLACE FUNCTION cs_parse_url_host(varchar) RETURNS varchar AS ' DECLARE v_url ALIAS FOR $1; --- 2732,2736 ---- ! CREATE OR REPLACE FUNCTION cs_parse_url_host(varchar) RETURNS varchar AS $$ DECLARE v_url ALIAS FOR $1; *************** *** 2697,2710 **** BEGIN v_host := NULL; ! a_pos1 := instr(v_url, ''//''); IF a_pos1 = 0 THEN ! RETURN ''''; -- Return a blank END IF; ! a_pos2 := instr(v_url,''/'',a_pos1 + 2); IF a_pos2 = 0 THEN v_host := substr(v_url, a_pos1 + 2); ! v_path := ''/''; RETURN v_host; END IF; --- 2742,2755 ---- BEGIN v_host := NULL; ! a_pos1 := instr(v_url, '//'); IF a_pos1 = 0 THEN ! RETURN ''; -- Return a blank END IF; ! a_pos2 := instr(v_url,'/',a_pos1 + 2); IF a_pos2 = 0 THEN v_host := substr(v_url, a_pos1 + 2); ! v_path := '/'; RETURN v_host; END IF; *************** *** 2713,2717 **** RETURN v_host; END; ! ' LANGUAGE plpgsql; --- 2758,2762 ---- RETURN v_host; END; ! $$ LANGUAGE plpgsql; *************** *** 2798,2802 **** ! CREATE OR REPLACE FUNCTION cs_create_job(integer) RETURNS integer AS ' DECLARE v_job_id ALIAS FOR $1; --- 2843,2847 ---- ! CREATE OR REPLACE FUNCTION cs_create_job(integer) RETURNS integer AS $$ DECLARE v_job_id ALIAS FOR $1; *************** *** 2809,2813 **** IF a_running_job_count > 0 THEN ! RAISE EXCEPTION ''Unable to create a new job: a job is currently running.''; END IF; --- 2854,2858 ---- IF a_running_job_count > 0 THEN ! RAISE EXCEPTION 'Unable to create a new job: a job is currently running.'; END IF; *************** *** 2821,2830 **** RETURN 1; ELSE ! RAISE NOTICE ''Job already running.''; END IF; RETURN 0; END; ! ' LANGUAGE plpgsql; --- 2866,2875 ---- RETURN 1; ELSE ! RAISE NOTICE 'Job already running.'; END IF; RETURN 0; END; ! $$ LANGUAGE plpgsql; *************** *** 2859,2863 **** quote_string(text) as described in . Constructs of the ! type EXECUTE ''SELECT * FROM $1''; will not work unless you use these functions. --- 2904,2908 ---- quote_string(text) as described in . Constructs of the ! type EXECUTE 'SELECT * FROM $1'; will not work unless you use these functions. *************** *** 2882,2888 **** ! CREATE FUNCTION foo(...) RETURNS integer AS ' ... ! ' LANGUAGE plpgsql STRICT IMMUTABLE; --- 2927,2933 ---- ! CREATE FUNCTION foo(...) RETURNS integer AS $$ ... ! $$ LANGUAGE plpgsql STRICT IMMUTABLE; *************** *** 2909,2913 **** -- ! CREATE FUNCTION instr(varchar, varchar) RETURNS integer AS ' DECLARE pos integer; --- 2954,2958 ---- -- ! CREATE FUNCTION instr(varchar, varchar) RETURNS integer AS $$ DECLARE pos integer; *************** *** 2916,2923 **** RETURN pos; END; ! ' LANGUAGE plpgsql; ! CREATE FUNCTION instr(varchar, varchar, varchar) RETURNS integer AS ' DECLARE string ALIAS FOR $1; --- 2961,2968 ---- RETURN pos; END; ! $$ LANGUAGE plpgsql; ! CREATE FUNCTION instr(varchar, varchar, varchar) RETURNS integer AS $$ DECLARE string ALIAS FOR $1; *************** *** 2958,2965 **** END IF; END; ! ' LANGUAGE plpgsql; ! CREATE FUNCTION instr(varchar, varchar, integer, integer) RETURNS integer AS ' DECLARE string ALIAS FOR $1; --- 3003,3010 ---- END IF; END; ! $$ LANGUAGE plpgsql; ! CREATE FUNCTION instr(varchar, varchar, integer, integer) RETURNS integer AS $$ DECLARE string ALIAS FOR $1; *************** *** 3019,3026 **** END IF; END; ! ' LANGUAGE plpgsql; ! --- 3064,3071 ---- END IF; END; ! $$ LANGUAGE plpgsql; ! Index: doc/src/sgml/plpython.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/plpython.sgml,v retrieving revision 1.22 diff -2 -c -r1.22 plpython.sgml *** doc/src/sgml/plpython.sgml 29 Nov 2003 19:51:37 -0000 1.22 --- doc/src/sgml/plpython.sgml 14 May 2004 17:05:08 -0000 *************** *** 50,54 **** CREATE FUNCTION myfunc(text) RETURNS text ! AS 'return args[0]' LANGUAGE plpythonu; --- 50,54 ---- CREATE FUNCTION myfunc(text) RETURNS text ! AS $$return args[0]$$ LANGUAGE plpythonu; *************** *** 231,235 **** ). For example: ! CREATE FUNCTION usesavedplan() RETURNS trigger AS ' if SD.has_key("plan"): plan = SD["plan"] --- 231,235 ---- ). For example: ! CREATE FUNCTION usesavedplan() RETURNS trigger AS $$ if SD.has_key("plan"): plan = SD["plan"] *************** *** 238,242 **** SD["plan"] = plan # rest of function ! ' LANGUAGE plpythonu; --- 238,242 ---- SD["plan"] = plan # rest of function ! $$ LANGUAGE plpythonu; Index: doc/src/sgml/pltcl.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/pltcl.sgml,v retrieving revision 2.29 diff -2 -c -r2.29 pltcl.sgml *** doc/src/sgml/pltcl.sgml 24 Jan 2004 23:06:29 -0000 2.29 --- doc/src/sgml/pltcl.sgml 14 May 2004 17:05:08 -0000 *************** *** 78,84 **** ! CREATE FUNCTION funcname (argument-types) RETURNS return-type AS ' # PL/Tcl function body ! ' LANGUAGE pltcl; --- 78,85 ---- ! CREATE FUNCTION funcname ! (argument-types) RETURNS return-type AS $$ # PL/Tcl function body ! $$ LANGUAGE pltcl; *************** *** 101,108 **** ! CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS ' if {$1 > $2} {return $1} return $2 ! ' LANGUAGE pltcl STRICT; --- 102,109 ---- ! CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS $$ if {$1 > $2} {return $1} return $2 ! $$ LANGUAGE pltcl STRICT; *************** *** 123,127 **** ! CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS ' if {[argisnull 1]} { if {[argisnull 2]} { return_null } --- 124,128 ---- ! CREATE FUNCTION tcl_max(integer, integer) RETURNS integer AS $$ if {[argisnull 1]} { if {[argisnull 2]} { return_null } *************** *** 131,135 **** if {$1 > $2} {return $1} return $2 ! ' LANGUAGE pltcl; --- 132,136 ---- if {$1 > $2} {return $1} return $2 ! $$ LANGUAGE pltcl; *************** *** 155,159 **** ); ! CREATE FUNCTION overpaid(employee) RETURNS boolean AS ' if {200000.0 < $1(salary)} { return "t" --- 156,160 ---- ); ! CREATE FUNCTION overpaid(employee) RETURNS boolean AS $$ if {200000.0 < $1(salary)} { return "t" *************** *** 163,167 **** } return "f" ! ' LANGUAGE pltcl; --- 164,168 ---- } return "f" ! $$ LANGUAGE pltcl; *************** *** 360,382 **** ! CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS ' if {![ info exists GD(plan) ]} { # prepare the saved plan on the first call ! set GD(plan) [ spi_prepare \\ ! "SELECT count(*) AS cnt FROM t1 WHERE num >= \\$1 AND num <= \\$2" \\ [ list int4 int4 ] ] } spi_execp -count 1 $GD(plan) [ list $1 $2 ] return $cnt ! ' LANGUAGE pltcl; ! Note that each backslash that Tcl should see must be doubled when ! we type in the function, since the main parser processes ! backslashes, too, in CREATE FUNCTION. We need backslashes inside ! the query string given to spi_prepare to ensure that ! the $n markers will be passed through to ! spi_prepare as-is, and not ! replaced by Tcl variable substitution. --- 361,382 ---- ! CREATE FUNCTION t1_count(integer, integer) RETURNS integer AS $$ if {![ info exists GD(plan) ]} { # prepare the saved plan on the first call ! set GD(plan) [ spi_prepare \ ! "SELECT count(*) AS cnt FROM t1 WHERE num >= \$1 AND num <= \$2" \ [ list int4 int4 ] ] } spi_execp -count 1 $GD(plan) [ list $1 $2 ] return $cnt ! $$ LANGUAGE pltcl; ! We need backslashes inside the query string given to ! spi_prepare to ensure that the ! $n markers will be passed ! through to spi_prepare as-is, and not replaced by Tcl ! variable substitution. ! *************** *** 426,430 **** ! SELECT 'doesn''t' AS ret --- 426,430 ---- ! SELECT $q$doesn't$q$ AS ret *************** *** 612,616 **** ! CREATE FUNCTION trigfunc_modcount() RETURNS trigger AS ' switch $TG_op { INSERT { --- 612,616 ---- ! CREATE FUNCTION trigfunc_modcount() RETURNS trigger AS $$ switch $TG_op { INSERT { *************** *** 626,630 **** } return [array get NEW] ! ' LANGUAGE pltcl; CREATE TABLE mytab (num integer, description text, modcnt integer); --- 626,630 ---- } return [array get NEW] ! $$ LANGUAGE pltcl; CREATE TABLE mytab (num integer, description text, modcnt integer); Index: doc/src/sgml/queries.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/queries.sgml,v retrieving revision 1.29 diff -2 -c -r1.29 queries.sgml *** doc/src/sgml/queries.sgml 3 Mar 2004 22:22:24 -0000 1.29 --- doc/src/sgml/queries.sgml 14 May 2004 17:05:08 -0000 *************** *** 632,638 **** CREATE TABLE foo (fooid int, foosubid int, fooname text); ! CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS ' SELECT * FROM foo WHERE fooid = $1; ! ' LANGUAGE SQL; SELECT * FROM getfoo(1) AS t1; --- 632,638 ---- CREATE TABLE foo (fooid int, foosubid int, fooname text); ! CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$ SELECT * FROM foo WHERE fooid = $1; ! $$ LANGUAGE SQL; SELECT * FROM getfoo(1) AS t1; Index: doc/src/sgml/rules.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/rules.sgml,v retrieving revision 1.34 diff -2 -c -r1.34 rules.sgml *** doc/src/sgml/rules.sgml 9 Mar 2004 05:05:40 -0000 1.34 --- doc/src/sgml/rules.sgml 14 May 2004 17:05:09 -0000 *************** *** 344,350 **** ! CREATE FUNCTION min(integer, integer) RETURNS integer AS ' SELECT CASE WHEN $1 < $2 THEN $1 ELSE $2 END ! ' LANGUAGE SQL STRICT; --- 344,350 ---- ! CREATE FUNCTION min(integer, integer) RETURNS integer AS $$ SELECT CASE WHEN $1 < $2 THEN $1 ELSE $2 END ! $$ LANGUAGE SQL STRICT; Index: doc/src/sgml/spi.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/spi.sgml,v retrieving revision 1.34 diff -2 -c -r1.34 spi.sgml *** doc/src/sgml/spi.sgml 1 Apr 2004 21:28:43 -0000 1.34 --- doc/src/sgml/spi.sgml 14 May 2004 17:05:09 -0000 *************** *** 2685,2689 **** CREATE FUNCTION execq(text, integer) RETURNS integer ! AS 'filename' LANGUAGE C; --- 2685,2689 ---- CREATE FUNCTION execq(text, integer) RETURNS integer ! AS $$filename$$ LANGUAGE C; Index: doc/src/sgml/syntax.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/syntax.sgml,v retrieving revision 1.91 diff -2 -c -r1.91 syntax.sgml *** doc/src/sgml/syntax.sgml 10 May 2004 22:44:43 -0000 1.91 --- doc/src/sgml/syntax.sgml 14 May 2004 17:05:09 -0000 *************** *** 241,252 **** escaping ! A string constant in SQL is an arbitrary sequence of characters ! bounded by single quotes ('), e.g., 'This ! is a string'. SQL allows single quotes to be embedded ! in strings by typing two adjacent single quotes, e.g., ! 'Dianne''s horse'. In ! PostgreSQL single quotes may ! alternatively be escaped with a backslash (\), ! e.g., 'Dianne\'s horse'. --- 241,284 ---- escaping ! A string constant in SQL is an arbitrary sequence of characters ! bounded by single quotes ('), e.g., 'This ! is a string', or dollar quotes, e.g. $q$This is a ! string$q$. SQL allows single quotes to be embedded in ! strings by typing two adjacent single quotes, e.g., ! 'Dianne''s horse'. In ! PostgreSQL single quotes may alternatively ! be escaped with a backslash (\), e.g., ! 'Dianne\'s horse'. With dollar quotes, this ! could be written as $$Dianne's horse$$ or ! $long_dollar_quote_string$Dianne's ! horse$long_dollar_quote_string$. Here is an example of ! how dollar quotes can nest: ! ! ! ! CREATE OR REPLACE FUNCTION has_bad_chars(TEXT) RETURNS BOOLEAN AS ! $function$ ! BEGIN ! RETURN ($1 ~ $q$[\t\r\n\v|\\]$q$); ! END; ! $function$ LANGUAGE plpgsql; ! ! ! ! Note that nesting requires a different identifier, as above, and ! only works when the quoted string will be re-parsed. ! ! ! Dollar quoting is not part of the SQL standard, but it is most ! useful in places, like function bodies, where the SQL standard does ! not apply. Please not that everything inside dollar quotes is ! passed literally. For example, inside dollar quotes, backslash is ! just another character with no magic attached. ! ! ! ! Dollar quotes are case sensitive, so ! $quote$This$quote$ is valid, but ! $QUOTE$This$quote$ is not. *************** *** 1009,1013 **** CREATE FUNCTION dept(text) RETURNS dept ! AS 'SELECT * FROM dept WHERE name = $1' LANGUAGE SQL; --- 1041,1045 ---- CREATE FUNCTION dept(text) RETURNS dept ! AS $$SELECT * FROM dept WHERE name = $1$$ LANGUAGE SQL; *************** *** 1464,1468 **** CREATE TABLE mytable(f1 int, f2 float, f3 text); ! CREATE FUNCTION getf1(mytable) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL; -- No cast needed since only one getf1() exists SELECT getf1(ROW(1,2.5,'this is a test')); --- 1496,1500 ---- CREATE TABLE mytable(f1 int, f2 float, f3 text); ! CREATE FUNCTION getf1(mytable) RETURNS int AS $$SELECT $1.f1$$ LANGUAGE SQL; -- No cast needed since only one getf1() exists SELECT getf1(ROW(1,2.5,'this is a test')); *************** *** 1473,1477 **** CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric); ! CREATE FUNCTION getf1(myrowtype) RETURNS int AS 'SELECT $1.f1' LANGUAGE SQL; -- Now we need a cast to indicate which function to call: SELECT getf1(ROW(1,2.5,'this is a test')); --- 1505,1509 ---- CREATE TYPE myrowtype AS (f1 int, f2 text, f3 numeric); ! CREATE FUNCTION getf1(myrowtype) RETURNS int AS $$SELECT $1.f1$$ LANGUAGE SQL; -- Now we need a cast to indicate which function to call: SELECT getf1(ROW(1,2.5,'this is a test')); Index: doc/src/sgml/trigger.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/trigger.sgml,v retrieving revision 1.35 diff -2 -c -r1.35 trigger.sgml *** doc/src/sgml/trigger.sgml 3 Mar 2004 22:22:24 -0000 1.35 --- doc/src/sgml/trigger.sgml 14 May 2004 17:05:09 -0000 *************** *** 546,550 **** CREATE FUNCTION trigf() RETURNS trigger ! AS 'filename' LANGUAGE C; --- 546,550 ---- CREATE FUNCTION trigf() RETURNS trigger ! AS $$filename$$ LANGUAGE C; Index: doc/src/sgml/xfunc.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/xfunc.sgml,v retrieving revision 1.82 diff -2 -c -r1.82 xfunc.sgml *** doc/src/sgml/xfunc.sgml 10 May 2004 22:44:43 -0000 1.82 --- doc/src/sgml/xfunc.sgml 14 May 2004 17:05:10 -0000 *************** *** 105,115 **** The body of an SQL function should be a list of one or more SQL ! statements separated by semicolons. Note that because the syntax ! of the CREATE FUNCTION command requires the body of the ! function to be enclosed in single quotes, single quote marks ! (') used ! in the body of the function must be escaped, by writing two single ! quotes ('') or a backslash (\') where each ! quote is desired. --- 105,115 ---- The body of an SQL function should be a list of one or more SQL ! statements separated by semicolons. Although dollar quoting ! obviates this, note that because the syntax of the CREATE ! FUNCTION command, if you choose not to use dollar ! quoting, i.e. the body of the function is enclosed in single quotes, ! you must escape single quote marks (') used in the body of ! the function, either by writing two single quotes ('') or ! with a backslash (\') where you desire each quote to be. *************** *** 131,134 **** --- 131,139 ---- + CREATE FUNCTION one() RETURNS integer AS $$ + SELECT 1 AS result; + $$ LANGUAGE SQL; + + -- Alternative syntax, now deprecated: CREATE FUNCTION one() RETURNS integer AS ' SELECT 1 AS result; *************** *** 141,144 **** --- 146,150 ---- 1 + *************** *** 157,163 **** ! CREATE FUNCTION add_em(integer, integer) RETURNS integer AS ' SELECT $1 + $2; ! ' LANGUAGE SQL; SELECT add_em(1, 2) AS answer; --- 163,169 ---- ! CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$ SELECT $1 + $2; ! $$ LANGUAGE SQL; SELECT add_em(1, 2) AS answer; *************** *** 174,183 **** ! CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS ' UPDATE bank SET balance = balance - $2 WHERE accountno = $1; SELECT 1; ! ' LANGUAGE SQL; --- 180,189 ---- ! CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS $$ UPDATE bank SET balance = balance - $2 WHERE accountno = $1; SELECT 1; ! $$ LANGUAGE SQL; *************** *** 196,205 **** ! CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS ' UPDATE bank SET balance = balance - $2 WHERE accountno = $1; SELECT balance FROM bank WHERE accountno = $1; ! ' LANGUAGE SQL; --- 202,211 ---- ! CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$ UPDATE bank SET balance = balance - $2 WHERE accountno = $1; SELECT balance FROM bank WHERE accountno = $1; ! $$ LANGUAGE SQL; *************** *** 222,229 **** ! CREATE FUNCTION clean_emp() RETURNS void AS ' DELETE FROM emp WHERE salary <= 0; ! ' LANGUAGE SQL; SELECT clean_emp(); --- 228,235 ---- ! CREATE FUNCTION clean_emp() RETURNS void AS $$ DELETE FROM emp WHERE salary <= 0; ! $$ LANGUAGE SQL; SELECT clean_emp(); *************** *** 259,265 **** ); ! CREATE FUNCTION double_salary(emp) RETURNS numeric AS ' SELECT $1.salary * 2 AS salary; ! ' LANGUAGE SQL; SELECT name, double_salary(emp.*) AS dream --- 265,271 ---- ); ! CREATE FUNCTION double_salary(emp) RETURNS numeric AS $$ SELECT $1.salary * 2 AS salary; ! $$ LANGUAGE SQL; SELECT name, double_salary(emp.*) AS dream *************** *** 305,314 **** ! CREATE FUNCTION new_emp() RETURNS emp AS ' ! SELECT text ''None'' AS name, 1000 AS salary, 25 AS age, ! point ''(2,2)'' AS cubicle; ! ' LANGUAGE SQL; --- 311,320 ---- ! CREATE FUNCTION new_emp() RETURNS emp AS $$ ! SELECT text 'None' AS name, 1000 AS salary, 25 AS age, ! point '(2,2)' AS cubicle; ! $$ LANGUAGE SQL; *************** *** 406,412 **** ! CREATE FUNCTION getname(emp) RETURNS text AS ' SELECT $1.name; ! ' LANGUAGE SQL; SELECT getname(new_emp()); --- 412,418 ---- ! CREATE FUNCTION getname(emp) RETURNS text AS $$ SELECT $1.name; ! $$ LANGUAGE SQL; SELECT getname(new_emp()); *************** *** 440,446 **** INSERT INTO foo VALUES (2, 1, 'Mary'); ! CREATE FUNCTION getfoo(int) RETURNS foo AS ' SELECT * FROM foo WHERE fooid = $1; ! ' LANGUAGE SQL; SELECT *, upper(fooname) FROM getfoo(1) AS t1; --- 446,452 ---- INSERT INTO foo VALUES (2, 1, 'Mary'); ! CREATE FUNCTION getfoo(int) RETURNS foo AS $$ SELECT * FROM foo WHERE fooid = $1; ! $$ LANGUAGE SQL; SELECT *, upper(fooname) FROM getfoo(1) AS t1; *************** *** 479,485 **** ! CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS ' SELECT * FROM foo WHERE fooid = $1; ! ' LANGUAGE SQL; SELECT * FROM getfoo(1) AS t1; --- 485,491 ---- ! CREATE FUNCTION getfoo(int) RETURNS SETOF foo AS $$ SELECT * FROM foo WHERE fooid = $1; ! $$ LANGUAGE SQL; SELECT * FROM getfoo(1) AS t1; *************** *** 506,512 **** ! CREATE FUNCTION listchildren(text) RETURNS SETOF text AS ! 'SELECT name FROM nodes WHERE parent = $1' ! LANGUAGE SQL; SELECT * FROM nodes; --- 512,518 ---- ! CREATE FUNCTION listchildren(text) RETURNS SETOF text AS $$ ! SELECT name FROM nodes WHERE parent = $1 ! $$ LANGUAGE SQL; SELECT * FROM nodes; *************** *** 559,565 **** from two arbitrary data type elements: ! CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS ' SELECT ARRAY[$1, $2]; ! ' LANGUAGE SQL; SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray; --- 565,571 ---- from two arbitrary data type elements: ! CREATE FUNCTION make_array(anyelement, anyelement) RETURNS anyarray AS $$ SELECT ARRAY[$1, $2]; ! $$ LANGUAGE SQL; SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray; *************** *** 590,596 **** return type, but the converse is not. For example: ! CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS ' SELECT $1 > $2; ! ' LANGUAGE SQL; SELECT is_greater(1, 2); --- 596,602 ---- return type, but the converse is not. For example: ! CREATE FUNCTION is_greater(anyelement, anyelement) RETURNS boolean AS $$ SELECT $1 > $2; ! $$ LANGUAGE SQL; SELECT is_greater(1, 2); *************** *** 600,606 **** (1 row) ! CREATE FUNCTION invalid_func() RETURNS anyelement AS ' SELECT 1; ! ' LANGUAGE SQL; ERROR: cannot determine result data type DETAIL: A function returning "anyarray" or "anyelement" must have at least one argument of either type. --- 606,612 ---- (1 row) ! CREATE FUNCTION invalid_func() RETURNS anyelement AS $$ SELECT 1; ! $$ LANGUAGE SQL; ERROR: cannot determine result data type DETAIL: A function returning "anyarray" or "anyelement" must have at least one argument of either type. *************** *** 660,664 **** CREATE FUNCTION square_root(double precision) RETURNS double precision ! AS 'dsqrt' LANGUAGE internal STRICT; --- 666,670 ---- CREATE FUNCTION square_root(double precision) RETURNS double precision ! AS $$dsqrt$$ LANGUAGE internal STRICT; Index: doc/src/sgml/ref/create_function.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/ref/create_function.sgml,v retrieving revision 1.55 diff -2 -c -r1.55 create_function.sgml *** doc/src/sgml/ref/create_function.sgml 29 Nov 2003 19:51:38 -0000 1.55 --- doc/src/sgml/ref/create_function.sgml 14 May 2004 17:05:10 -0000 *************** *** 26,30 **** | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | [EXTERNAL] SECURITY INVOKER | [EXTERNAL] SECURITY DEFINER ! | AS 'definition' | AS 'obj_file', 'link_symbol' } ... --- 26,30 ---- | CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT | [EXTERNAL] SECURITY INVOKER | [EXTERNAL] SECURITY DEFINER ! | AS ' | $dollar_quote$definition' | $dollar_quote$ | AS 'obj_file', 'link_symbol' } ... *************** *** 375,379 **** CREATE FUNCTION add(integer, integer) RETURNS integer ! AS 'select $1 + $2;' LANGUAGE SQL IMMUTABLE --- 375,379 ---- CREATE FUNCTION add(integer, integer) RETURNS integer ! AS $$select $1 + $2;$$ LANGUAGE SQL IMMUTABLE Index: doc/src/sgml/ref/create_type.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/ref/create_type.sgml,v retrieving revision 1.49 diff -2 -c -r1.49 create_type.sgml *** doc/src/sgml/ref/create_type.sgml 12 Feb 2004 23:41:02 -0000 1.49 --- doc/src/sgml/ref/create_type.sgml 14 May 2004 17:05:10 -0000 *************** *** 467,472 **** CREATE TYPE compfoo AS (f1 int, f2 text); ! CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS ! 'SELECT fooid, fooname FROM foo' LANGUAGE SQL; --- 467,473 ---- CREATE TYPE compfoo AS (f1 int, f2 text); ! CREATE FUNCTION getfoo() RETURNS SETOF compfoo AS $$ ! SELECT fooid, fooname FROM foo ! $$ LANGUAGE SQL; Index: doc/src/sgml/ref/select.sgml =================================================================== RCS file: /projects/cvsroot/pgsql-server/doc/src/sgml/ref/select.sgml,v retrieving revision 1.76 diff -2 -c -r1.76 select.sgml *** doc/src/sgml/ref/select.sgml 9 Mar 2004 16:57:47 -0000 1.76 --- doc/src/sgml/ref/select.sgml 14 May 2004 17:05:10 -0000 *************** *** 939,945 **** ! CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS ' SELECT * FROM distributors WHERE did = $1; ! ' LANGUAGE SQL; SELECT * FROM distributors(111); --- 939,945 ---- ! CREATE FUNCTION distributors(int) RETURNS SETOF distributors AS $$ SELECT * FROM distributors WHERE did = $1; ! $$ LANGUAGE SQL; SELECT * FROM distributors(111); *************** *** 948,954 **** 111 | Walt Disney ! CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS ' SELECT * FROM distributors WHERE did = $1; ! ' LANGUAGE SQL; SELECT * FROM distributors_2(111) AS (f1 int, f2 text); --- 948,954 ---- 111 | Walt Disney ! CREATE FUNCTION distributors_2(int) RETURNS SETOF record AS $$ SELECT * FROM distributors WHERE did = $1; ! $$ LANGUAGE SQL; SELECT * FROM distributors_2(111) AS (f1 int, f2 text);