I have a package which contains more than 2000 lines. My question is can I create the packages dynamically by using execute immediate?
You'll need to use the associative array DBMS_SQL interface assuming that "2000 lines" equates to more than 32k worth of text. That means that you'll need to load the DDL into multiple elements of an associative array before passing that to the DBMS_SQL.PARSE method. Something like this works
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_sql dbms_sql.varchar2a;
3 c integer;
4 begin
5 l_sql(1) := 'CREATE OR REPLACE PACKAGE pkg_dynamic ';
6 l_sql(2) := 'AS ';
7 l_sql(3) := ' PROCEDURE my_proc;';
8 l_sql(4) := 'END;';
9 c := dbms_sql.open_cursor;
10 dbms_sql.parse( c, l_sql, 1, 4, true, dbms_sql.native );
11* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> desc pkg_dynamic;
PROCEDURE MY_PROC
But I would strongly question why you are trying to use dynamic SQL to create packages in the first place. It doesn't generally make sense to write code that turns around and generates more code. You wouldn't, for example, generally want to write a Java application that turned around and wrote and compiled another Java application that someone would then run.
Related
Tell me, please, why does the empty value come?
To send a request, I use SoapUI 5.5.
But :body is not null.
Do I need to do something in the settings of ORDS?
DECLARE
--b_body BLOB := :body;
c_body CLOB := :body_text;
BEGIN
if :body_text is null then
htp.print('EMPTY');
end if;
END;
As it says in documentation https://docs.oracle.com/en/database/oracle/oracle-rest-data-services/18.3/aelig/implicit-parameters.html#GUID-76A23568-EA67-4375-A4AA-880E1D160D27, for each implicit parameter :body and :body_text "if it is dereferenced more than once, then the second and subsequent dereferences will appear to be empty."
So, change your code like this:
DECLARE
--b_body BLOB := :body;
c_body CLOB := :body_text;
BEGIN
if c_body is null then
htp.print('EMPTY');
end if;
END;
If I remember correct it's not a good idea to use both binds in 1 code block...
If ORDS checks that you're using :body, :body_text is not populated (I think because of the overall performance of converting a blob to clob).
So just use :body_text and you should be fine!
This symptom may result from creating RESTful Services via older versions of the APEX SQL Workshop interface. APEX 5.1 certainly exhibits this behaviour, possibly others. If you are unable to upgrade APEX, use SQL Developer to create your ORDS modules.
I have a little problem when calling a Postgresql function in Delphi with FireDAC.
The Postgresql function has the following definition:
CREATE OR REPLACE FUNCTION public."pgpDecryptMe" (
todecode text
)
RETURNS text AS
$body$
DECLARE
PGPPrivate TEXT;
BEGIN
...
So it expects a "text" value and returns a "text" value.
I can call it with a long text parameter (over 900 character) and it returns the correct value in any sql admin tool without any problems.
select "pgpDecryptMe"('c1c04c030...a378624e6a659a20765') as Decrypt
But calling it in Delphi with the following code:
PGQuery.SQL.Text := 'select "pgpDecryptMe"(:test) as testvalue';
PGQuery.ParamByName('test').AsString := 'c1c04c030...a378624e6a659a20765';
PGQuery.Open();
Gives me the following error message:
[FireDAC][DatS]-2. Object [id] is not found
I googled and searched here but can't find any solution for the problem.
It is probably something very small I can't see :-(
I am working with Delphi XE7 and PostgreSQL 9.3
Ok, now I got it working.
It looks like it needed an additional index field name which doesn't really make sense because it just returned one value...
So it worked when I changed my code to the following:
PGQuery.SQL.Text := 'select "pgpDecryptMe"(:test) as testvalue';
PGQuery.ParamByName('test').AsString := 'c1c04c030...a378624e6a659a20765';
PGQuery.IndexFieldNames := 'testvalue';
PGQuery.Open();
I just noticed that I could alter my stored procedure code with a misspelled user defined function in it.
I noticed that at 1st time I execute the SP.
Is there any way to get a compile error when an SP include an invalid user-defined function name in it?
At compile time? No.
You can, however, use some of SQL's dependency objects (if using MS SQL) to find problems just after deployment, or as part of your beta testing. Aaron Bertran has a pretty nice article rounding up the options, depending upon the version of SQL Server.
Here is an example using SQL Server 2008 sys object called sql_expression_dependencies
CREATE FUNCTION dbo.scalarTest
(
#input1 INT,
#input2 INT
)
RETURNS INT
AS
BEGIN
-- Declare the return variable here
DECLARE #ResultVar int
-- Add the T-SQL statements to compute the return value here
SELECT #ResultVar = #input1 * #input2
-- Return the result of the function
RETURN #ResultVar
END
GO
--Fn Works!
SELECT dbo.ScalarTest(2,2)
GO
CREATE PROCEDURE dbo.procTest
AS
BEGIN
SELECT TOP 1 dbo.scalarTest(3, 3) as procResult
FROM sys.objects
END
GO
--Sproc Works!
EXEC dbo.procTest
GO
--Remove a dependency needed by our sproc
DROP FUNCTION dbo.scalarTest
GO
--Does anything have a broken dependency? YES
SELECT OBJECT_NAME(referencing_id) AS referencing_entity_name,
referenced_entity_name, *
FROM sys.sql_expression_dependencies
WHERE referenced_id IS NULL --dependency is missing
GO
--Does it work? No
EXEC dbo.procTest
GO
I'm developing a database using the Red Gate SQL Developer tools. SQL Test, the SSMS add-in that runs tSQLt tests, lacks a way to rename test classes.
I have a test called [BackendLayerCustomerAdministrationTests].[test uspMaintainCustomerPermissions throws error when PermissionValue is missing or empty].
The name is so long it breaks Deployment Manager.
2013-12-05 18:48:40 +00:00 ERROR The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
There are other unwieldly test names in this class, so I want to start by shortening the class name.
A more succinct class name would be CustomerTests.
sp_rename is no help here.
EXECUTE sys.sp_rename
#objname = N'BackendLayerCustomerAdministrationTests',
#newname = N'CustomerTests';
Msg 15225, Level 11, State 1, Procedure sp_rename, Line 374
No item by the name of 'BackendLayerCustomerAdministrationTests' could be found in the current database 'ApiServices', given that #itemtype was input as '(null)'.
How do I change it?
tSQLt test classes are schemas with a special extended property.
Cade Roux's great solution for renaming schemas is to create a new schema, transfer all the objects, then drop the old schema.
If we did that here we'd lose the extended property.
Let's adapt it for the tSQLt framework.
How to rename a tSQLt test class
Create a new test class.
EXECUTE tSQLt.NewTestClass
#ClassName = 'CustomerTests';
You should see the old class and the new class together in the tSQLt.TestClasses view.
SELECT *
FROM tSQLt.TestClasses;
Name SchemaId
----------------------------------------- ----------
SQLCop 7
BackendLayerCustomerAdministrationTests 10
CustomerTests 14
Cade used Chris Shaffer's select variable concatenation trick to build a list of transfer statements, and print the result.
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql = #sql +
N'ALTER SCHEMA CustomerTests
TRANSFER BackendLayerCustomerAdministrationTests.' + QUOTENAME(name) + N';' +
CHAR(13) + CHAR(10)
FROM sys.objects
WHERE SCHEMA_NAME([schema_id]) = N'BackendLayerCustomerAdministrationTests';
PRINT #sql;
Ugly, but effective.
Copy the output and execute as a new query.
ALTER SCHEMA CustomerTests
TRANSFER BackendLayerCustomerAdministrationTests.[test uspMaintainCustomer validate merged data];
ALTER SCHEMA CustomerTests
TRANSFER BackendLayerCustomerAdministrationTests.[test uspMaintainCustomerPermissions throws error when PermissionValue is missing or empty];
I've shown only two tests here, but it should work for all of them.
Now drop the old test class.
EXECUTE tSQLt.DropClass
#ClassName = N'BackendLayerCustomerAdministrationTests';
The old class should be gone from view.
SELECT *
FROM tSQLt.TestClasses;
Name SchemaId
----------------------------------------- ----------
SQLCop 7
CustomerTests 14
Run all your tests again to check that it worked.
EXECUTE tSQLt.RunAll;
+----------------------+
|Test Execution Summary|
+----------------------+
|No|Test Case Name |Result |
+--+----------------------------------------------------------------------------+-------+
|1|[CustomerTests].[test uspMaintainCustomer throws error on missing APIKey] |Success|
|2|[CustomerTests].[test uspMaintainCustomerPermissions validate merged data] |Success|
|3|[SQLCop].[test Decimal Size Problem] |Success|
|4|[SQLCop].[test Procedures Named SP_] |Success|
|5|[SQLCop].[test Procedures using dynamic SQL without sp_executesql] |Success|
|6|[SQLCop].[test Procedures with ##Identity] |Success|
|7|[SQLCop].[test Procedures With SET ROWCOUNT] |Success|
-------------------------------------------------------------------------------
Test Case Summary: 7 test case(s) executed, 7 succeeded, 0 failed, 0 errored.
-------------------------------------------------------------------------------
Success!
Sorry to come into this so late! I'm a developer who's working on SQL Test.
We've just added the ability to rename test classes to the latest version of SQL Test.
http://www.red-gate.com/products/sql-development/sql-test/
It's now as simple as right clicking on the context menu for a test class, or pressing F2:
Please bear in mind that this option will not appear for old versions of tSQLt. To upgrade, right click on the database to uninstall the framework, then do Add database... to re-add it (the right-most button in the window):
Alternatively, you could just call a new procedure in tSQLt called tSQLt.RenameClass, which is what SQL Test calls behind the scenes.
Please let us know if you have any issues with this!
David
What is your workflow like? If you have all your tests for that test class in one script with exec tSQLt.NewTestClass 'BackendLayerCustomerAdministrationTests' then you can just find and replace the testclass name and you are done.
e.g.
EXEC tSQLt.DropClass 'BackendLayerCustomerAdministrationTests'
GO
EXEC tSQLt.NewTestClass 'CustomerTests'
GO
CREATE PROC [CustomerTests].[test_Insert_AddsACustomer]
AS
etc, etc
This will work because the EXEC tSQLt.NewTestClass 'CustomerTests' will drop all objects in the testclass and they will be recreated as the rest of the script runs.
Simplest is probably:
EXEC tSQLt.RenameClass 'old test class name', 'new test class name';
See the tSQLt docs for RenameClass
It seems Red-gate have added that ability to SQL Test since this question was posted, but the raw SQL code is somehow leaner and cleaner (whether or not you use the excellent SQL Test)
Is it possible to have database wide constants?
What I want is to define a constant like:
UPDATE_CONSTANT = 1
INSERT_CONSTANT = 2
DELETE_CONSTANT = 3
and then use it in for example a trigger like:
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
AS
BEGIN
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', DELETE_CONSTANT;
END;
You could use a generator:
SET GENERATOR DELETE_CONSTANT TO 3;
...
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', GEN_ID(DELETE_CONSTANT, 0);
Update: yes, using a generator for this purpose is dangerous, as they can be changed.
However, in FireBird 3.0 Alpha 1 this risk can be eliminated using access rights: Grants access on generators.
I don't think there is an easy way for declaring constants.
I could be done by creating you own DLL for user defined function, and lmake a function for each constant.
I Think the Idea using generators as "global" constants is briliant.
But you can make a "local constant" to make your code a bit more readable:
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
AS
DECLARE VARIABLE DELETE_CONSTANT INTEGER;
BEGIN
DELETE_CONSTANT = 1;
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', DELETE_CONSTANT;
END;
Use a single row table with triggers that prevent insertion and deletion from it. Having to read from it certainly does not makes code clearer but it helps to implement such "constants". Also remove write permissions from everybody but sysdba
You can implement some simple preprocesor of yours scripts that converts constants to values..
triggers.presql
#DELETE_CONSTANT = 1
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
BEGIN
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', DELETE_CONSTANT;
END;
triggers.sql
CREATE TRIGGER AD_PRJ_PROJECTS FOR PRJ_PROJECT
ACTIVE AFTER DELETE
POSITION 1
BEGIN
EXECUTE PROCEDURE SP_ADD_HISTORY 'PRJ_PROJECT', 1;
END;