Must declare the scalar variable #bundleTransfer when it's a table type? [duplicate] - sql-server-2008

Here's my user-defined table type...
CREATE TYPE [dbo].[FooType] AS TABLE(
[Bar] [INT],
)
This is what ive had to do in my table-valued function to return the type:
CREATE FUNCTION [dbo].[GetFoos]
RETURN #FooTypes TABLE ([Bar] [INT])
INSERT INTO #FooTypes (1)
RETURN
Basically, im having to re-declare my type definition in the RETURN statement of the function. Isnt there a way i can simply declare the type in the RETURN statement?
I would have thought this would work:
CREATE FUNCTION [dbo].[GetFoos]
RETURN #FooTypes [FooType]
INSERT INTO #FooTypes (1)
RETURN
Cannot find any help on MSDN/Google regarding this....anyone?
EDIT
I unmarked my answer, and bumping this question - as i am encountering the same scenario 6 months later.
Does anyone have any idea if it's possible to return a user defined table type from a table valued function? If not, is there a better workaround other than what i have done? (re-declare the type again).

Even though you can not return the UDTT from a function, you can return a table variable and receive it in a UDTT as long as the schema match. The following code is tested in SQL Server 2008 R2
-- Create the UDTT
CREATE TYPE dbo.MyCustomUDDT AS TABLE
(
FieldOne varchar (512),
FieldTwo varchar(1024)
)
-- Declare your variables
DECLARE #uddt MyCustomUDDT;
DECLARE #Modifieduddt MyCustomUDDT;
// Call the function
INSERT INTO #Modifieduddt SELECT * FROM dbo.MyUDF(#uddt);
Function signature
CREATE FUNCTION dbo.MyUDF(#localUDDT MyCustomUDDT)
RETURNS #tableVar TABLE
(
FieldOne varchar (512),
FieldTwo varchar(1024)
)
AS
BEGIN
--Modify your variable here
RETURN
END
Hopefully this will help somebody.

Ok - so it cant be done.
Easy enough to duplicate the table definition in the return type (with the use of scripting).
Still - hopefully this issue gets rectified in the next version of SQL Server.

The syntax for CREATE FUNCTION indicates that the only way to define a table return type is by listing columns and types, a <table_type_definition>. Even SQL Server "Denali" has the same definition for <table_type_definition>. Although strangely, it's syntax doesn't include multi-statement Table valued functions, or anything else that references this fragment.

I do not believe this is possible. You cannot use a UDTT as the return type of a Scalar-Valued Function because it is not a scalar value. You also cannot replace the table declaration of a Table-Valued Function with a UDTT. Repeating the table definition seems to be the only option. If we knew why you were doing this, perhaps we could find an alternative.

Related

How To emulate MySQL Function with OUT parameter

As far as I understand MySQL does not support functions with OUT (as well as IN / INOUT) parameter types.
I am creating a function
DROP FUNCTION IF EXISTS `GETGOSTAUTHRUS`;
CREATE DEFINER = `root`#`localhost` FUNCTION `GETGOSTAUTHRUS`(`PublID` int)
RETURNS varchar(1024) CHARSET utf8
BEGIN
RETURN .....;
END;
This function is called from SELECT statement:
SELECT
GETGOSTAUTHRUS(p.ID) `AuthList`,.......
FROM....
Everything works fine. However I need to extend the function and return another value of varchar type but I can not declare out prefix varchar(50) variable in the same way as I do in procedure declaration:
CREATE procedure `GETGOSTAUTHRUS`(PublID int, OUT prefix varchar(50))
BEGIN
The only way out I have invented is to declare another function and call it separately, but it seems not optimal as the second function will fetch and process the same data from the tables.
How can I manage the issue?
Functions are supposed to return only one value, you might have the wrong approach here. We don't know what you're exactly trying to do, so we can't tell if we're dealing with a XY problem here. There may be better solutions to your overall problem.
You can solve this however with user-defined variables. These are session bound, so make sure to reset them in your function.

Need to run a pl/pgsql fn that runs an INSERT

Here is what I have, I am trying to create an insert fn that loads row data essentially into a per-existing table. I also want to run a check on specific column data to make sure the source data is not invalid.
The problem I seem to be having is getting it to run successfully. For some reason I can't seem to get this to work and I have tried various ways and have researched diligently within the site(some that are close but, don't quite give me what I need). Here is basically what I have and want to achieve. I know it may be basic, so thanks in advance.
CREATE OR REPLACE FUNCTION Schema.insert_fn (arg_1 character varying , arg_2 integer)
RETURNS SETOF CHARACTER VARYING AS
$BODY$
BEGIN
--should this insert use some kind of temp table?
insert into <schema>.table1 (character varying, integer)
values (arg_1 character varying, arg_2 integer);
--If I wanted to run some sort of check on say arg_2
If(select distinct (arg_2) from <schema>.table2 where invalid_date is not null)
THEN
raise notice 'Data has been invalidated';
END IF;
Return 'complete';
END;
$BODY$
Update
First, it told me that my return need to have 'NEXT' or 'QUERY'
RETURN cannot have a parameter in function returning set;
use RETURN NEXT or RETURN QUERY at or near "'complete'"
Once I do this, of course the function will complete. However, when I call it I get an error saying for example:
invalid input syntax for type boolean: "arg_1"
I apologize if I come off a little vague. Obviously I can't give you the complete context of the arg names as they relate to what I am doing. I appreciate any help.
Update
I also receive this error:
more than one row returned by a subquery used as an expression
I did research on this issue as well and simply cannot relate any kind of solution to at least get this to work, meaning; when I call it say, with no arguments I receive this error.
Update #ErwinBrandstetter. I communicated that wrong. I meant if
'col2 = arg_2 and invalid_date is NOT null' to raise an exception
What is happening is that the 'EXIST' statement will take any instance to where a row is found. I tried 'WHERE EXIST' and I got an error. The problem I figure is that they(validated and invalidated data) share the same unique id whether and it makes the EXIST statement true(I didn't provide this info mind you).
Update
#ErwinBrandstetter It now operates successfully. Looks like all I needed to do was seperate the two conditions. Thanks.
IF EXISTS (condition)
THEN
INSERT
ELSEIF EXISTS (invalidated data condition)
THEN
RAISE EXCEPTION'DATA IS INVALIDATED';
END IF;
END;
Might work like this:
CREATE OR REPLACE FUNCTION schema.insert_fn (_arg1 text, _arg2 integer)
RETURNS text AS -- not SETOF!
$func$
BEGIN
INSERT INTO <schema>.table1 (col1, col2) -- names here! not types
SELECT _arg1, _arg2 -- again: names! cast only if needed
WHERE NOT EXISTS ( -- only if _arg2 not invalidated
SELECT 1
FROM <schema>.table2
WHERE col2 = _arg2
AND invalid_date IS NOT NULL
);
IF NOT FOUND THEN -- No exception yet, no INSERT either --> invalidated
RAISE EXCEPTION 'Data >>%<< has been invalidated.', _arg2;
END IF;
RETURN 'complete'::text; -- return value probably useless
END
$func$ LANGUAGE plpgsql
The most prominent error was that you declared the function to return a SETOF values, while you actually only return a single value. I might just use RETURNs void, since the return value does not carry information as is.
Read the manual here and here.
Use a SELECT with your INSERT to apply additional conditions directly.
There is more. See comments in code above.

Why can I reference a non-existing function within another function?

Here, I will show that referencing a non-existing function from another function is possible and SQL Server doesn't check it until the execution time:
USE [SomeDataBase];
SELECT dbo.Booo();
Obviously, if you don't have function Booo then an error will be generated regarding function Booo is not recognized. This isn't a surprise though!
Now, try this:
CREATE FUNCTION dbo.Foo()
RETURNS INT
AS
BEGIN
DECLARE #Temp INT
SET #Temp = (SELECT dbo.Booo())
RETURN 1
END
Surprisingly, this scrip creates the function Foo despite the fact that the Booo function doesn't exit.
Any idea?
Why do you think that's a bug? Since the code isn't actually executed until you run the Foo function, there's a case to be made that that is the point where the check should be made.
Maybe you write your functions in a top-down manner, rather than a bottom-up manner, and you want to write the upper levels first, drilling down to specifics later.
Unless it's documented to work one way and it works another way, it's not a bug, just a disagreement between you and Microsoft :-)
If you do
CREATE FUNCTION dbo.Foo()
RETURNS INT
WITH SCHEMABINDING
AS
BEGIN
DECLARE #Temp INT
SET #Temp = (SELECT dbo.Booo())
RETURN 1
END
You get your desired error and the function is not created. That does make altering the definition of dbo.Booo in the future more painful however (need to drop dbo.Foo first).
You can also use a SQL Server Data Tools project to validate things like referencing non existent objects/columns without using schemabinding.

Table name as a PostgreSQL function parameter

I want to pass a table name as a parameter in a Postgres function. I tried this code:
CREATE OR REPLACE FUNCTION some_f(param character varying) RETURNS integer
AS $$
BEGIN
IF EXISTS (select * from quote_ident($1) where quote_ident($1).id=1) THEN
return 1;
END IF;
return 0;
END;
$$ LANGUAGE plpgsql;
select some_f('table_name');
And I got this:
ERROR: syntax error at or near "."
LINE 4: ...elect * from quote_ident($1) where quote_ident($1).id=1)...
^
********** Error **********
ERROR: syntax error at or near "."
And here is the error I got when changed to this select * from quote_ident($1) tab where tab.id=1:
ERROR: column tab.id does not exist
LINE 1: ...T EXISTS (select * from quote_ident($1) tab where tab.id...
Probably, quote_ident($1) works, because without the where quote_ident($1).id=1 part I get 1, which means something is selected. Why may the first quote_ident($1) work and the second one not at the same time? And how could this be solved?
Before you go there: for only few, known tables names, it's typically simpler to avoid dynamic SQL and spell out the few code variants in separate functions or in a CASE construct.
That said, what you are trying to achieve can be simplified and improved:
CREATE OR REPLACE FUNCTION some_f(_tbl regclass, OUT result integer)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE format('SELECT (EXISTS (SELECT FROM %s WHERE id = 1))::int', _tbl)
INTO result;
END
$func$;
Call with schema-qualified name (see below):
SELECT some_f('myschema.mytable'); -- would fail with quote_ident()
Or:
SELECT some_f('"my very uncommon table name"');
Major points
Use an OUT parameter to simplify the function. You can directly select the result of the dynamic SQL into it and be done. No need for additional variables and code.
EXISTS does exactly what you want. You get true if the row exists or false otherwise. There are various ways to do this, EXISTS is typically most efficient.
You seem to want an integer back, so I cast the boolean result from EXISTS to integer, which yields exactly what you had. I would return boolean instead.
I use the object identifier type regclass as input type for _tbl. That does everything quote_ident(_tbl) or format('%I', _tbl) would do, but better, because:
.. it prevents SQL injection just as well.
.. it fails immediately and more gracefully if the table name is invalid / does not exist / is invisible to the current user. (A regclass parameter is only applicable for existing tables.)
.. it works with schema-qualified table names, where a plain quote_ident(_tbl) or format(%I) would fail because they cannot resolve the ambiguity. You would have to pass and escape schema and table names separately.
It only works for existing tables, obviously.
I still use format(), because it simplifies the syntax (and to demonstrate how it's used), but with %s instead of %I. Typically, queries are more complex so format() helps more. For the simple example we could as well just concatenate:
EXECUTE 'SELECT (EXISTS (SELECT FROM ' || _tbl || ' WHERE id = 1))::int'
No need to table-qualify the id column while there is only a single table in the FROM list. No ambiguity possible in this example. (Dynamic) SQL commands inside EXECUTE have a separate scope, function variables or parameters are not visible there - as opposed to plain SQL commands in the function body.
Here's why you always escape user input for dynamic SQL properly:
db<>fiddle here demonstrating SQL injection
Old sqlfiddle
If at all possible, don't do this.
That's the answer—it's an anti-pattern. If the client knows the table it wants data from, then SELECT FROM ThatTable. If a database is designed in a way that this is required, it seems to be designed sub-optimally. If a data access layer needs to know whether a value exists in a table, it is easy to compose SQL in that code, and pushing this code into the database is not good.
To me this seems like installing a device inside an elevator where one can type in the number of the desired floor. After the Go button is pressed, it moves a mechanical hand over to the correct button for the desired floor and presses it. This introduces many potential issues.
Please note: there is no intention of mockery, here. My silly elevator example was *the very best device I could imagine* for succinctly pointing out issues with this technique. It adds a useless layer of indirection, moving table name choice from a caller space (using a robust and well-understood DSL, SQL) into a hybrid using obscure/bizarre server-side SQL code.
Such responsibility-splitting through movement of query construction logic into dynamic SQL makes the code harder to understand. It violates a standard and reliable convention (how a SQL query chooses what to select) in the name of custom code fraught with potential for error.
Here are detailed points on some of the potential problems with this approach:
Dynamic SQL offers the possibility of SQL injection that is hard to recognize in the front end code or the back end code alone (one must inspect them together to see this).
Stored procedures and functions can access resources that the SP/function owner has rights to but the caller doesn't. As far as I understand, without special care, then by default when you use code that produces dynamic SQL and runs it, the database executes the dynamic SQL under the rights of the caller. This means you either won't be able to use privileged objects at all, or you have to open them up to all clients, increasing the surface area of potential attack to privileged data. Setting the SP/function at creation time to always run as a particular user (in SQL Server, EXECUTE AS) may solve that problem, but makes things more complicated. This exacerbates the risk of SQL injection mentioned in the previous point, by making the dynamic SQL a very enticing attack vector.
When a developer must understand what the application code is doing in order to modify it or fix a bug, he'll find it very difficult to get the exact SQL query being executed. SQL profiler can be used, but this takes special privileges and can have negative performance effects on production systems. The executed query can be logged by the SP but this increases complexity for questionable benefit (requiring accommodating new tables, purging old data, etc.) and is quite non-obvious. In fact, some applications are architected such that the developer does not have database credentials, so it becomes almost impossible for him to actually see the query being submitted.
When an error occurs, such as when you try to select a table that doesn't exist, you'll get a message along the lines of "invalid object name" from the database. That will happen exactly the same whether you're composing the SQL in the back end or the database, but the difference is, some poor developer who's trying to troubleshoot the system has to spelunk one level deeper into yet another cave below the one where the problem exists, to dig into the wonder-procedure that Does It All to try to figure out what the problem is. Logs won't show "Error in GetWidget", it will show "Error in OneProcedureToRuleThemAllRunner". This abstraction will generally make a system worse.
An example in pseudo-C# of switching table names based on a parameter:
string sql = $"SELECT * FROM {EscapeSqlIdentifier(tableName)};"
results = connection.Execute(sql);
While this does not eliminate every possible issue imaginable, the flaws I outlined with the other technique are absent from this example.
Inside plpgsql code, The EXECUTE statement must be used for queries in which table names or columns come from variables. Also the IF EXISTS (<query>) construct is not allowed when query is dynamically generated.
Here's your function with both problems fixed:
CREATE OR REPLACE FUNCTION some_f(param character varying) RETURNS integer
AS $$
DECLARE
v int;
BEGIN
EXECUTE 'select 1 FROM ' || quote_ident(param) || ' WHERE '
|| quote_ident(param) || '.id = 1' INTO v;
IF v THEN return 1; ELSE return 0; END IF;
END;
$$ LANGUAGE plpgsql;
I know this is an old thread, but I ran across it recently when trying to solve the same problem - in my case, for some fairly complex scripts.
Turning the entire script into dynamic SQL is not ideal. It's tedious and error-prone work, and you lose the ability to parameterize: parameters must be interpolated into constants in the SQL, with bad consequences for performance and security.
Here's a simple trick that lets you keep the SQL intact if you only need to select from your table - use dynamic SQL to create a temporary view:
CREATE OR REPLACE FUNCTION some_f(_tbl varchar) returns integer
AS $$
BEGIN
drop view if exists myview;
execute format('create temporary view myview as select * from %s', _tbl);
-- now you can reference myview in the SQL
IF EXISTS (select * from myview where myview.id=1) THEN
return 1;
END IF;
return 0;
END;
$$ language plpgsql;
The first doesn't actually "work" in the sense that you mean, it works only in so far as it does not generate an error.
Try SELECT * FROM quote_ident('table_that_does_not_exist');, and you will see why your function returns 1: the select is returning a table with one column (named quote_ident) with one row (the variable $1 or in this particular case table_that_does_not_exist).
What you want to do will require dynamic SQL, which is actually the place that the quote_* functions are meant to be used.
If the question was to test if the table is empty or not (id=1), here is a simplified version of Erwin's stored proc :
CREATE OR REPLACE FUNCTION isEmpty(tableName text, OUT zeroIfEmpty integer) AS
$func$
BEGIN
EXECUTE format('SELECT COALESCE ((SELECT 1 FROM %s LIMIT 1),0)', tableName)
INTO zeroIfEmpty;
END
$func$ LANGUAGE plpgsql;
If you want table name, column name and value to be dynamically passed to function as parameter
use this code
create or replace function total_rows(tbl_name text, column_name text, value int)
returns integer as $total$
declare
total integer;
begin
EXECUTE format('select count(*) from %s WHERE %s = %s', tbl_name, column_name, value) INTO total;
return total;
end;
$total$ language plpgsql;
postgres=# select total_rows('tbl_name','column_name',2); --2 is the value
I have 9.4 version of PostgreSQL and I always use this code:
CREATE FUNCTION add_new_table(text) RETURNS void AS
$BODY$
begin
execute
'CREATE TABLE ' || $1 || '(
item_1 type,
item_2 type
)';
end;
$BODY$
LANGUAGE plpgsql
And then:
SELECT add_new_table('my_table_name');
It works good for me.
Attention! Above example is one of those which shows "How do not if we want to keep safety during querying the database" :P

Return array in stored function

I am trying to use the result of a stored function in a WHERE statement in MySQL (5.x), but it fails because in the function I am selecting values from a table into an INT variable and then returning them/it, which obviously doesn't work if the SELECT returns more than 1 row. I've tried returning a TABLE (as I understood TABLE means array in MySQL) but that didn't work either.
Is there any way that I could do something like:
SELECT ID FROM myTable WHERE ID IN my_function(params);
Thank you.
This cannot be done...
First, you cannot use a stored function to return multiple results - you would need to use a stored procedure.
The MySQL docs state:
Statements that return a result set can be used within a stored procedure but not within a stored function.
Second, you cannot use a stored procedure in a query - see this SO question.
Have you considered using 'HAVING ...' at the end of your query?