Why does using the IF-ENDIF block in this DB2 function cause - db2

Why does using the IF statement in this function result in error 42601 [SQL0104], as shown below..? This is DB2-400 for i v7r3m0.
SQL Error [42601]: [SQL0104] Token <END-OF-STATEMENT> was not valid. Valid tokens: ;.
The code example I provide below executes without error until the IF statement is uncommented. I've tried moving the semicolons around and even removing them, but then the errors get worse and begin pointing to later statements being invalid.
I've checked the IBM documentation for IF on v7r3, and my syntax seems to be correct. Other code examples follow the same syntax as that and mine. I'm stumped.
CREATE OR REPLACE FUNCTION F_CERT.CERT_UPC_COMMON_DESC (UPC NUMERIC(14))
RETURNS INTEGER
LANGUAGE SQL
GLOBAL DETERMINISTIC
NO EXTERNAL ACTION
NOT FENCED
ALLOW PARALLEL
BEGIN
DECLARE RETVAL INTEGER DEFAULT 0 ;
DECLARE UPC_COUNT INTEGER DEFAULT 0 ;
DECLARE UPC_LIST CURSOR FOR
SELECT COUNT(*)
FROM F_CERTOB.BEERXT
WHERE BXUPCR=UPC
;
OPEN UPC_LIST ;
FETCH UPC_LIST INTO UPC_COUNT ;
CLOSE UPC_LIST ;
-- IF UPC_COUNT > 0 THEN
-- -- OTHER
-- -- COMMANDS
SET RETVAL = UPC_COUNT ;
-- END IF ;
RETURN RETVAL ;
END ;
SELECT F_CERT.CERT_UPC_COMMON_DESC (793936791660) AS C FROM SYSIBM.SYSDUMMY1 ;
EDIT:
Here is a second example; a trimmed-down version. As with the code above, everything is fine until the IF statements are uncommented:
CREATE OR REPLACE FUNCTION F_CERT.CERT_UPC_COMMON_DESC (UPC NUMERIC(14))
RETURNS INTEGER
LANGUAGE SQL
BEGIN
DECLARE RETVAL INTEGER DEFAULT 0 ;
-- IF 1=1 THEN
SET RETVAL = 1 ;
-- ELSE
SET RETVAL = 100 ;
-- END IF ;
RETURN RETVAL ;
END ;
SELECT F_CERT.CERT_UPC_COMMON_DESC (12345) AS C FROM SYSIBM.SYSDUMMY1 ;

I've continued searching since my OP, and I finally found the exact solution
here.
The issue is the statement delimiter. A conflict arises when the function contains flow-control and similarly structured statements that span multiple lines. In my code example, the compiler stops at END IF; and thinks that's all there is, instead of continuing to the END; of the function.
My SQL client is DBeaver 5.2.5, and it does have some functionality to run statements & scripts in different ways. This article gave me the idea to try running my CREATE script with the different actions of DBeaver, and I discovered I could make it work if I highlighted the whole script from CREATE to END but not including the trailing semicolon. Then I used Execute Statement {Ctrl+Enter} instead of the usual Execute Sctipt {Alt+X}. And it finally worked..!! But that seemed "kludgy" so I continued searching for a solution.
Then I found the imperfect, but perfectly acceptable solution in the main article mentioned above. I followed the directions to change the settings in my DBeaver client, and edited my code as shown below (notice the # characters). And then it finally worked..!! I could run the whole script, including the SELECT statement, and was able to do so with the usual Execute Statement {Alt+X} keystroke that I've become accustomed to.
CREATE OR REPLACE FUNCTION F_CERT.CERT_UPC_COMMON_DESC (UPC NUMERIC(14))
RETURNS INTEGER
LANGUAGE SQL
BEGIN
DECLARE RETVAL INTEGER DEFAULT 0;
IF 1=0 THEN
SET RETVAL = 1;
ELSE
SET RETVAL = 100;
END IF;
RETURN RETVAL;
END #
SELECT F_CERT.CERT_UPC_COMMON_DESC (793936791660) AS C FROM SYSIBM.SYSDUMMY1 #

Related

psql variable substitution for postgres script with functions

I have a Postgres script that I have that I intend to run against multiple databases. The sample SQL has functions and tables like
CREATE FUNCTION point() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.the_geom:=ST_SetSRID(geom, :CRS) ;
RETURN NEW;
END
$$;
CREATE TABLE admin (
gid integer NOT NULL,
geom geometry(Polygon,:CRS)
);
I have put a variable inside so that I can substitute it on runtime as
psql -d db -p 5432 -U username -h localhost -f test.sql --variable=CRS=3857
Why does the variable only get properly substituted in table definitions and not function definition
https://www.postgresql.org/docs/current/static/app-psql.html#APP-PSQL-INTERPOLATION
Therefore, a construction such as ':foo' doesn't work to produce a
quoted literal from a variable's value (and it would be unsafe if it
did work, since it wouldn't correctly handle quotes embedded in the
value).
a function definition is put between quotes - in your case $$ instead of ', so it fails. You can change ST_SetSRID(geom, :CRS) to ST_SetSRID(geom, $$:CRS) to see it start being handled again (of course ruining the function body)
As a workaround you can try using bash variable instead:
vao#vao-VirtualBox:~$ export CRS=33
vao#vao-VirtualBox:~$ psql t << EOF
> CREATE OR REPLACE FUNCTION point() RETURNS trigger
> LANGUAGE plpgsql
> AS \$\$
> BEGIN
> NEW.the_geom:=ST_SetSRID(geom, $CRS) ;
> RETURN NEW;
> END
> \$\$;
> \sf point()
> EOF
CREATE FUNCTION
CREATE OR REPLACE FUNCTION public.point()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
NEW.the_geom:=ST_SetSRID(geom, 33) ;
RETURN NEW;
END
$function$
Of course it can be scripted to a file that you can call as bash with argument
Shell substitution, as Vao Tsun suggests, might work fine for small psql scripts, but it's still not a general solution. For instance, shells are not aware of psql and pgSQL syntaxes and may substitute sub-strings that are not meant to be substituted in the psql script:
-- If CA and AU are undefined environment variable (likely),
-- the following SQL, after shell substitution, will result in:
-- ... VALUES ('Australian Dollar', ''), (Canadian Dollar, '');
INSERT (name, symbol) INTO currencies VALUES
('Australian Dollar', '$AU'),
('Canadian Dollar', '$CA');
This will become unmanageable with large scripts without additional measures. A better approach is to use string concatenation. Something like this would be ideal:
-- This won't work; syntax error
CREATE FUNCTION point() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.the_geom := ST_SetSRID(geom, $$ || :'CRS' || $$);
RETURN NEW;
END
$$;
Unfortunately, pgSQL syntax expects literal strings as function bodies, not arbitrary string expressions. Fortunately, however, PostgreSQL implements as part of its SQL syntax implicit concatenation between single-quoted strings separated by only whitespaces and at least one line break:
-- This gets parsed as "SELECT 'mystring';"
SELECT 'my'
'string';
The catch though is that it only works with single-quoted strings, not dollar-quoted strings. The following will work fine, but it's an eye-sore:
-- This will work, but it's ugly
CREATE FUNCTION point() RETURNS trigger
LANGUAGE plpgsql
AS
-- Mind the trailing space below
'BEGIN '
'NEW.the_geom := ST_SetSRID(geom, $$'
:'CRS'
'$$);'
'RETURN NEW;'
'END';
This is perhaps a more eye-pleasing variant:
-- This will work
-- (assign appropriate ownership and permissions if CRS contains sentitive data)
CREATE FUNCTION _get_crs() RETURNS text LANGUAGE sql AS 'SELECT $$'
:'crs'
'$$';
CREATE FUNCTION point() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.the_geom := ST_SetSRID(geom, _get_crs());
RETURN NEW;
END
$$;
And here's another variant:
-- This will also work
-- (may expose sensitive data in the catalog)
SET "app.crs" = :'crs';
CREATE FUNCTION point() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.the_geom := ST_SetSRID(geom, current_setting('app.crs'));
RETURN NEW;
END
$$;
The function body is a sting literal. psql has hardly any string modification functionality. However, PostgreSQL has plenty of it. With psql variables and \gset (since PostgreSQL 9.3) you can utilize this to first build your command and then execute it:
--for demo purpose set CRS directly. The OP did pass it from outside to psql
\set CRS 1234
--here we wrap the whole CREATE FUNCTION call in a literal separated by $_$
--this improves the readabilty and covers the inner $$. The result will be
--stored in psql variable func. This is workaround for a multi-line \set
SELECT $_$
CREATE FUNCTION point() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.the_geom:=ST_SetSRID(geom, :CRS) ;
RETURN NEW;
END
$$;
$_$ AS func \gset
--replace the substring ':CRS' with content of variable CRS
--and store the result in func again
SELECT replace(:'func', ':CRS', :'CRS') AS func \gset
--we can now have a look to the resulting DDL
\echo :func
--and we can execute it:
:func
--clean up psql variables
\unset func
\unset CRS
with the help of \gexec (since PostgreSQL 9.6) we can also combine the last two steps:
--replace the substring ':CRS' with content of variable CRS
--and execute the result
SELECT replace(:'func', ':CRS', :'CRS') \gexec
or even do all in one command:
SELECT replace($_$
CREATE FUNCTION point() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW.the_geom:=ST_SetSRID(geom, :CRS) ;
RETURN NEW;
END
$$;
$_$, ':CRS', :'CRS') \gexec

plpgsql code that worked in postgresql 8.3 fails in 9.2

I have a problem with an array parameter going into a plpgsql function. My code works in PostgreSQL 8.3, but fails when called on a 9.2.1 server.
I have written a dummy function that shows the problem. This is based on some of the first plpgsql code I have written. I know it is ugly, so if it is not possible to escape my quotes in a way that works for both server versions, I am open to suggestions on rewriting this code so that it works on both server versions. Actually I am open to suggestions no matter what. I am not too good at plpgsql
So here's the dummy code that demonstrates the problem:
CREATE OR REPLACE FUNCTION get_test(collection text[])
RETURNS text AS
$BODY$
DECLARE
counter int8; directive text; condition text; querytype text;
BEGIN
counter = array_lower(collection, 1);
WHILE (counter <= array_upper(collection, 1)) LOOP
SELECT INTO directive "?column?"[counter] FROM (SELECT collection) AS foo ;
counter = counter + 1;
SELECT INTO condition "?column?"[counter] FROM (SELECT collection) AS foo ;
counter = counter + 1;
SELECT into querytype "?column?"[counter] FROM (SELECT collection) AS foo ;
counter = counter + 1;
END LOOP;
RETURN 'dummy';
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION get_test(text[]) OWNER TO postgres;
The collection parameter is built in sets of three (a simple string, a SQL condition, and another string). Here is an example call that works in 8.3 and fails in 9.2:
select * from get_test('{"dynamic","(tr.PROJECT_NAME = \'SampleProject\')","1"}')
This gives back 'dummy' as expected on 8.3, but fails on 9.2 with:
ERROR: syntax error at or near "SampleProject"
The caret points to the S of SampleProject, right after the single-quote, so I know I am
not escaping the single quotes in the embedded SQL correctly.
I have tried this without the backslashes in front of the single quotes, with two backslashes, and finally with two single quotes, all to no avail. The original code is called by a Java client, but I have been testing this under pgadmin3 (version 1.16), connecting to two servers of the versions in question, trying different things.
Any ideas as to how I can make this call work?
Most likely reason is a different setting for standard_conforming_strings.
Try this call:
The SQL-standard way (and recommended in PostgreSQL) to escape single quotes inside a single-quoted string literal is to double them:
SELECT *
FROM get_test('{dynamic,(tr.PROJECT_NAME = ''SampleProject''),1}'::text[])
OR:
SELECT *
FROM get_test('{dynamic,"(tr.PROJECT_NAME = ''SampleProject'')",1}'::text[])
Or you can resort to dollar-quoting to avoid multiple layers of escaping quotes
SELECT *
FROM get_test($${dynamic,"(tr.PROJECT_NAME = 'SampleProject')",1}$$::text[])
Or even an ARRAY constructor:
SELECT *
FROM get_test(ARRAY['dynamic','(tr.PROJECT_NAME = ''SampleProject'')','1'])

SQL Server Management Studio Transaction and Variable

In SSMS 2012, after setting Options > Query Execution > ANSI > SET IMPLICIT_TRANSACTIONS, cf this SO post
I have the following code in a query window:
begin transaction
select ##TRANCOUNT
begin
declare #someNumber int; set #someNumber = 1;
print #someNumber;
end
rollback
When I select the whole block and press Execute, I see the expected result, i.e. 1.
However, when I select the first 4 lines and execute, then select line 5, i.e. print #someNumber;, I got the following message:
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "#someNumber".
What is exactly the scope of the variable?
I'm baffled. Can someone shed any light or point me to the right direction please?
The variable is scoped per batch.
The scope of a local variable is the batch in which it is declared.
Each press of "Execute" is a batch. So, for the 2nd run, it isn't declared
What are doing with 2 runs is this
--start of batch 1
begin transaction
select ##TRANCOUNT
begin
declare #someNumber int; set #someNumber = 1;
--end of batch 1
GO --separate batch after this
--start of batch 2
print #someNumber;
end
rollback
--end of batch 2
In SSMS, a variable lives for the duration of the execution.
If you execute the declaration only, it dies when the execution ends.
If you then execute the print statement of the variable, it was not declared within this execution so it does not exists.

When should I not use a semicolon?

Or: What is not a T-SQL statement?
Except to resolve ambiguity, T-SQL syntax does not require a semicolon to terminate a statement. Despite this, Itzik Ben-Gan recommends using a semicolon to terminate a T-SQL statement because it makes code cleaner, more readable, easier to maintain, and more portable.
I don't know a precise definition of what a valid T-SQL statement is, so I might be confused here. But as far as I know, a BEGIN...END block is a T-SQL statement, so should be terminated by a semicolon. For example:
IF OBJECT_ID('tempdb.dbo.#TempTable') IS NOT NULL
BEGIN
DROP TABLE #TempTable;
END;
The code example in Microsoft's BEGIN...END documentation supports this conjecture:
USE AdventureWorks2008R2;
GO
BEGIN TRANSACTION;
GO
IF ##TRANCOUNT = 0
BEGIN
SELECT FirstName, MiddleName
FROM Person.Person WHERE LastName = 'Adams';
ROLLBACK TRANSACTION;
PRINT N'Rolling back the transaction two times would cause an error.';
END;
ROLLBACK TRANSACTION;
PRINT N'Rolled back the transaction.';
GO
/*
Rolled back the tranaction.
*/
Itzik Ben-Gan contradicts this in the code example of Excercise 1-1 of T-SQL Fundamentals:
SET NOCOUNT ON;
USE TSQLFundamentals2008;
IF OBJECT_ID('dbo.Nums', 'U') IS NOT NULL DROP TABLE dbo.Nums;
CREATE TABLE dbo.Nums(n INT NOT NULL PRIMARY KEY);
DECLARE #i AS INT = 1;
BEGIN TRAN
WHILE #i <= 100000
BEGIN
INSERT INTO dbo.Nums VALUES(#i);
SET #i = #i + 1;
END
COMMIT TRAN
SET NOCOUNT OFF;
Microsoft's Transact-SQL Syntax Conventions document states that the semicolon "will be required in a future version" of T-SQL.
Commenting on Microsoft's intention to require the semicolon in a future version of T-SQL, Itzik notes some exceptions that aren't supposed to be terminated:
So far it was a requirement to use a semicolon only in specific cases. Now it looks like the plan is to make it a required terminator for all* T-SQL statements in some future version of SQL Server.
(*) Naturally there are cases that aren’t supposed to be terminated with a semicolon; those include (but are not limited to):
BEGIN
BEGIN TRAN
IF
ELSE
WHILE
BEGIN TRY
END TRY
BEGIN CATCH
Itzik seems to be consistent with himself, but Microsoft itself does not follow his recommendations. Compare Microsoft's BEGIN TRANSACTION; and Itzik's BEGIN TRAN in the previous examples.
In the code I maintain, I have seen even the BEGIN keyword terminated by semicolon:
IF #HasWidget = 0x1
BEGIN;
SELECT WidgetID
FROM tbWidgets;
END;
I believe a T-SQL parser may consider the semicolon following the BEGIN keyword to terminate an empty statement rather than terminate the BEGIN keyword itself; I don't believe that BEGIN itself is a valid T-SQL statement.
This conjecture is supported by the fact that SQL Server 2008 successfully parses and executes the following query:
SELECT 0;;
It's so confusing because there is no widely available specification of the T-SQL language, like the Java Language Specification for Java, so nowhere is there a formal definition of a T-SQL statement.
Am I wrong? Does such a specification exist for T-SQL, and is it publicly available?
Otherwise, should just I believe what Itzik says?
T-SQL syntax does not require a semicolon to terminate a statement.
Actually, this is deprecated1. I can't remember for sure, but I think you can still get away with not using them in the upcoming SQL Server 2012, but some version after that will likely require a semi-colon for every statement. Using a semi-colon is also technically required by the ansi standard. The point is that now is the time to get in the habit of using one for every statement.
As a practical matter, I don't expect them to follow through with this directly. Rather, I expect SQL Server Management Studio and other development tools to first start issuing warnings instead of errors, perhaps for several versions. This will help developers find and fix all the old non-compliant code. But that doesn't lessen the message: semi-colons are coming, and soon.
For a simple heuristic on when not to use a semi-colon, think of the code as if it were a procedural language that used curly brackets for blocks, like C/C++. Statements that would be paired with an opening (not closing) curly bracket if written in the procedure language should not get a semi-colon.
1It's almost all the way at the bottom of the page
Summary, based on the OP's original, quoted list.
Yes semi-colon:
BEGIN TRAN;
No semi-colon:
BEGIN
IF
ELSE
WHILE
BEGIN TRY
END TRY
BEGIN CATCH
Also, use them after END and END CATCH.
Details:
BEGIN TRAN is a statement and should be terminated with a semi-colon.
Microsoft's documentation notes the optional semi-colon:
BEGIN { TRAN | TRANSACTION }
[ { transaction_name | #tran_name_variable }
[ WITH MARK [ 'description' ] ]
]
[ ; ]
Microsoft's example has semi-colons:
BEGIN TRAN T1;
UPDATE table1 ...;
BEGIN TRAN M2 WITH MARK;
UPDATE table2 ...;
SELECT * from table1;
COMMIT TRAN M2;
UPDATE table3 ...;
COMMIT TRAN T1;
Both of the above are from:
https://msdn.microsoft.com/en-us/library/ms188929(v=sql.90).aspx
They match the current documentation:
https://msdn.microsoft.com/en-us/library/ms188929(v=sql.120).aspx
As for BEGIN...END, the Microsoft documentation does not provide clear guidance.
The definition has no semi-colon:
BEGIN
{
sql_statement | statement_block
}
END
However, their example shows a semi-colon after END:
IF ##TRANCOUNT = 0
BEGIN
SELECT FirstName, MiddleName
FROM Person.Person WHERE LastName = 'Adams';
ROLLBACK TRANSACTION;
PRINT N'Rolling back the transaction two times would cause an error.';
END;
https://msdn.microsoft.com/en-us/library/ms190487.aspx
That trailing semi-colon is not consistent with Microsoft's own documentation for IF control of flow language construct:
IF Boolean_expression
{ sql_statement | statement_block }
[ ELSE
{ sql_statement | statement_block } ]
Neither that definition nor their code example shows any semi-colon:
DECLARE #compareprice money, #cost money
EXECUTE Production.uspGetList '%Bikes%', 700,
#compareprice OUT,
#cost OUTPUT
IF #cost <= #compareprice
BEGIN
PRINT 'These products can be purchased for less than
$'+RTRIM(CAST(#compareprice AS varchar(20)))+'.'
END
ELSE
PRINT 'The prices for all products in this category exceed
$'+ RTRIM(CAST(#compareprice AS varchar(20)))+'.'
https://msdn.microsoft.com/en-us/library/ms182717(v=sql.110).aspx
However, their ELSE documentation, while also not showing any semi-colon in the definition, does show one in the example, after the final END.
Definition:
IF Boolean_expression { sql_statement | statement_block }
[ ELSE { sql_statement | statement_block } ]
Example:
IF 1 = 1 PRINT 'Boolean_expression is true.'
ELSE PRINT 'Boolean_expression is false.' ;
https://msdn.microsoft.com/en-us/library/ms182587(v=sql.110).aspx
The ANSI standard doesn't resolve the ambiguity because these are non-standard extensions:
Control-of-flow statements are not covered by the ANSI SQL standard
because these are proprietary SQL extensions. The SQL Server Books
Online is sketchy on the subject and many of the examples (as of this
writing) are inconsistent and do not always include statement
terminators. Furthermore, control-of-flow statement blocks are
confusing due to the many variations, nesting, and optional BEGIN/END
specifications.
http://www.dbdelta.com/always-use-semicolon-statement-terminators/
However, the behavior of the server sheds some light. The following is not a syntax error in SQL Server 2005:
DECLARE #foo int;
IF #foo IS NULL
BEGIN
WITH Blah AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah;
END
So the BEGIN itself does not require a semi-colon. However, the following does produce a syntax error in SQL Server 2005:
DECLARE #foo int;
IF #foo IS NULL
BEGIN
WITH Blah AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah;
END
WITH Blah2 AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah2;
The above results in this error:
Msg 319, Level 15, State 1, Line 13 Incorrect syntax near the keyword
'with'. If this statement is a common table expression or an
xmlnamespaces clause, the previous statement must be terminated with a
semicolon.
It also throws that error in SQL Server 2008 R2.
It gets even more confusing. Microsoft's documentation for TRY...CATCH shows an optional semi-colon after the END CATCH, and their examples are consistent with that.
BEGIN TRY
{ sql_statement | statement_block }
END TRY
BEGIN CATCH
[ { sql_statement | statement_block } ]
END CATCH
[ ; ]
However, if you have a CTE immediately after a BEGIN TRY, without a semi-colon, it will throw an error.
BEGIN TRY
WITH Blah AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah;
END TRY
BEGIN CATCH
END CATCH
In SQL Server 2008 R2, the above batch throws this error:
Msg 319, Level 15, State 1, Line 2 Incorrect syntax near the keyword
'with'. If this statement is a common table expression, an
xmlnamespaces clause or a change tracking context clause, the previous
statement must be terminated with a semicolon.
The error implies that BEGIN TRY is a statement (which it isn't), and that a semi-colon "fixes" the issue (which it does). That's right, this works:
BEGIN TRY;
WITH Blah AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah;
END TRY
BEGIN CATCH
END CATCH
However, Microsoft says that's not good practice:
Posted by Microsoft on 12/29/2009 at 12:11 PM I am resolving the
corresonding SQL11 bug as "by design". Here is the explanation:
The semicolon between END TRY and BEGIN CATCH should not be allowed,
because they are actually not different statements, but parts of the
same TRY-CATCH statement. We only allow semicolons when they separate
two statements in a sequence.
A word of explanation why then we allow semicolons after BEGIN TRY and
BEGIN CATCH. These keywords serve as opening "parentheses" that start
an embedded statement sequence. Semicolons after BEGIN TRY/BEGIN CATCH
get parsed as part of that embedded sequence, with the first statement
in the sequence being empty. While we allow this syntax, I would not
recommend it as a good coding practice because it creates a wrong
impression of BEGIN TRY/BEGIN CATCH being independent, standalone
statements.
The recommended way to handle that situation is with an extra BEGIN...END for clarity:
BEGIN TRY
BEGIN
WITH Blah AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah;
END
END TRY
BEGIN CATCH
END CATCH
However, that END before the END TRY should probably have a semi-colon. After all, this will throw an error:
BEGIN TRY
BEGIN
WITH Blah AS
(
SELECT
'a' AS a
)
SELECT
a
FROM Blah;
END
WITH Blah2 AS
(
SELECT
'b' AS b
)
SELECT
b
FROM Blah2;
END TRY
BEGIN CATCH
END CATCH
Maybe always preceding a CTE WITH a semi-colon isn't so silly.
The only situation in which I frequently using a semicolon is when using Common Table Expressions via the WITH keyword - and only then because the WITH keyword must be preceded by a semicolon otherwise it returns an error. In those cases, I write
;WITH [exp]...
i.e. I precede the WITH with a semicolon, rather than terminate the previous statement.
Semicolon usage in SQL seems to be very rare; I occasionally see it after a stored procedure or function declaration by that is the exception rather than the rule. Of all the developers I've worked with I don't believe any have really used the semicolon in the way that you described.
Statements like
BEGIN;
SELECT WidgetID
FROM tbWidgets;
END;
are hard to understand - if BEGIN; is considered a statement independent of its corresponding END;, why is SELECT WidgetID not a valid statement independent of its corresponding FROM?

PostgreSQL Syntax error in PGAdmin

I am new to PostgreSQL and am using the query tool in PGAdmin. I'm trying to run pgsql queries that use variables, but I can't seem to get the syntax right.
Here's a sample query that gives a syntax error:
DECLARE
num INTEGER;
BEGIN
num := 3;
PRINT num;
END;
Update:
Ok, let me try and explain. I come from a SQL server background. In the management studio, I can open a query window and play with (T)-SQL queries.
For example, I can write something like this:
DECLARE #num INT
SET #num = 3
SELECT #num
I know this is a dumb example, but I'm just trying to declare a variable and do something with it. I'm trying to familiarise myself with PL/PGSQL.
Update, again:
It's me again. I'm trying the script below and get a "[ERROR ] 7.0-2: syntax error, unexpected character". Is this meant to work in PGAdmin?
DECLARE
num INTEGER;
BEGIN
num := 3;
RAISE NOTICE '%', num;
END;
You can use the do statement. For example:
do $$
declare
num integer := 10;
begin
RAISE INFO 'VARIABLE: %', num;
end;
$$language plpgsql;
When you use pgadmin you have to use the button EXECUTE QUERY instead of
Execute pdScript, as it is explained here:
http://postgresql.1045698.n5.nabble.com/PgAmin3-Anonymous-code-block-can-t-be-executed-by-pressing-quot-Execute-PG-script-quot-button-td5771073.html
The documentation for do statements is here:
http://www.postgresql.org/docs/9.3/static/sql-do.html
Just to rephrase and "concretize" what others say:
There are no inline procedures in PostgreSQL. There is also no PRINT statement. You have to:
CREATE OR REPLACE FUNCTION test() RETURNS void AS $$
DECLARE
num INTEGER;
BEGIN
num := 3;
RAISE NOTICE '%', num;
END;
$$ LANGUAGE plpgsql;
SELECT test();
If you're trying to print out num (say, for debugging), you could try:
RAISE NOTICE '%', num;
http://www.postgresql.org/docs/8.4/static/plpgsql-errors-and-messages.html
PRINT doesn't mean anything in PL/pgSQL.
I have no idea what you are trying to achieve. PostgreSQL doesn't support this kind of syntax. Similar keywords (except PRINT?!) are in PL/pgSQL which is procedural language for building FUNCTIONS, not for writing stand-alone SQL queries.
Postgres doesn't support anything like that by itself (yet). psql (the official command line client) has some rudimentary scripting.
The best option for you is pgAdmin which already has scripting built-in.