Replacement of Context_Info SQL Azure - sql-server-2008

I am currently using the CONTEXT_INFO property of the Master database for storing the logged in username to use it later in Table Triggers for auditing.
While migrating to SQL Azure, the issue of Cross-Database connections popped and I couldn't find direct solutions to this issue.
Following are the Issue Details:
I call Stored Procedure XXX from Data Access Layer and pass the Username as Parameter
The username is used to set the CONTEXT_INFO value in XXX
The CONTEXT_INFO value is then used in Tables Insert/Update/Delete Triggers to Store Username for Application Auditing
Solutions that I found so far:
Create Table In Database to work as CONTEXT_INFO
Use 2 Connection Strings in Data Access Layer, one for Master Database (to set CONTEXT_INFO) and the other is for the application and execute the SET CONTEXT_INFO each time before opening the connection to my application
But I find both solutions risky, specially when expanding the Database over multiple SQL Azure Databases in the future.
Appreciate your support.

The approach I took is shown below. On trick was to check to see running not running on SQL Azure, then we would need to call 'SET CONTEXT_INFO ...'. This allows the same code to be execute on local SQL Server Express and Azure without changes.
Create a table to store the context info (not in master but in the same database)
CREATE TABLE [dbo].[ContextInfo] (
[ContextInfo] varbinary(128) not null,
[ApplicationUsername] nvarchar(128) not null,
[UpdatedAt] datetime NOT NULL,
CONSTRAINT [PK_UserContextInfo] PRIMARY KEY CLUSTERED ([ContextInfo] ASC)
)
Create a stored procedure to 'Set Context Info' which is called from application
CREATE PROCEDURE [dbo].[SetContextInfo]
#ApplicationUsername nvarchar(128)
AS
SET NOCOUNT ON
-- Remove all context items older than an 5 minutes ago
DELETE
FROM [dbo].[ContextInfo]
WHERE [UpdatedAt] < DATEADD(mi, -5, GETUTCDATE())
--
-- Use the MERGE command to do an update/insert
-- See: http://technet.microsoft.com/en-us/library/bb510625.aspx
--
IF SERVERPROPERTY('edition') <> 'SQL Azure'
BEGIN
DECLARE #b varbinary(128)
SET #b = CONVERT(varbinary(128),newid())
EXEC sp_executesql #statement=N'SET CONTEXT_INFO #b',#params=N'#b varbinary(128)',#b=#b
END
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
MERGE [dbo].[ContextInfo] AS target
USING (SELECT #ContextInfo, #ApplicationUsername) AS source ([ContextInfo], [ApplicationUsername])
ON (target.[ContextInfo] = source.[ContextInfo])
WHEN MATCHED THEN
UPDATE SET [ApplicationUsername] = source.[ApplicationUsername], [UpdatedAt] = GETUTCDATE()
WHEN NOT MATCHED THEN
INSERT ([ContextInfo], [ApplicationUsername], [UpdatedAt])
VALUES (source.[ContextInfo], source.[ApplicationUsername], GETUTCDATE());
Create a stored procedure to 'Get Context Info'
CREATE PROCEDURE [dbo].[GetContextInfo]
AS
SET NOCOUNT ON
DECLARE #ContextInfo varbinary(128)
SELECT #ContextInfo = CONTEXT_INFO()
SELECT [ApplicationUsername]
FROM [dbo].[ContextInfo]
WHERE [ContextInfo] = #ContextInfo
GO
In trigger source, use:
DECLARE #UserContext TABLE ([Username] VARCHAR(128))
INSERT INTO #UserContext (Username)
EXEC [dbo].[GetContextInfo]
Now you have the username stored in the table variable. In case changes are applied by an administrator outside of your application, you may also want to check if the username was not set and default to something like *SYSTEM_USER*.

Related

MS SQL Linked Servers And Using In FROM Clause

I have a very basic test stored procedure, shown below. This proc is reading data in a different database, in a different server. To do this I am using a linked server. From what I have read, I need to change the FROM clause to this:
[linked server name].[database name].[schema name].[table name]
However, I would like to pass in the linked server name and database name as parameters and use them in my FROM clause. I am not concerned with injection attacks, etc. I will be passing this in from a config file.
create PROC [dbo].[SelectTEST]
#GU UNIQUEIDENTIFIER,
#LINKED_SERVER_NAME nvarchar(max),
#DATABASE_NAME nvarchar(max)
AS
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN TRAN
SELECT [GU]
FROM '[' + #LINKED_SERVER_NAME +'].['+ #DATABASE_NAME + '].[Test Table] '
WHERE ([GU] = #GU OR #GU IS NULL)
COMMIT
This is a big mess of syntax errors. Is it possible to pass in these parameters and use in my stored procedure? I would have to make this change to a bunch of different procs, so sorta trying to find the a succinct solution...

Exception while calling stored procedure from jdbc

Calling a stored procedure results in this exception:
SQLException1 java.sql.SQLException: User does not have access to
metadata required to determine stored procedure parameter types. If
rights can not be granted, configure connection with
"noAccessToProcedureBodies=true" to have driver generate parameters
that represent INOUT strings irregardless of actual parameter types.
To resolve this, I tried:
Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306
/database?useInformationSchema=true&noAccessToProcedureBodies=true",
"user_name", "pasword");
But it still does not work.
I am using shared hosting.
I am using
Software version: 5.0.91-community-log - MySQL Community Edition (GPL)
Protocol version: 10
Java 1.6
mysql-connector-java-5.1.14-bin.jar
One of my stored procedures is:
DROP PROCEDURE IF EXISTS `share_message`
DELIMITER //
CREATE PROCEDURE share_message(in messageid1 int(200),in received_by1 int(20),
in sent_by1 int(20),in shared_of1 int(20),author1 int(20), OUT query_status1 TINYINT)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
-- ERROR
SET query_status1 = -1;
rollback;
END;
DECLARE EXIT HANDLER FOR SQLWARNING
BEGIN
-- WARNING
SET query_status1 = -1;
rollback;
END;
START TRANSACTION;
SET query_status1 = 0;
INSERT INTO post_message_users(messageid,received_by,sent_by,shared_of,author)
VALUES(messageid1,received_by1,sent_by1,shared_of1,author1);
UPDATE post_messages SET total_share=total_share+1 WHERE messageid=messageid1;
SET query_status1 =1;
COMMIT;
END//
DELIMITER ;
This is working properly with my local database.
It seems that the stored procedure you are attempting to use needs access to MySQL's INFORMATION_SCHEMA. That's a (fake) database built in every MySQL server; it's used to fetch descriptions of tables, columns, indexes, and the like.
It looks like the user id you're using doesn't have access to the INFORMATION_SCHEMA. That's understandable on a hosting service.
Go on MyPhpAdmin and try a query like this to be sure about that.
SELECT table_schema, table_name
FROM information_schema.columns
WHERE column_name = 'something'
AND table_schema = 'your database name'
If you get some kind of error saying you don't have permission, this is definitely your problem.
You could try rewriting your stored proc, or you could ask your hosting service to grant you the appropriate priv.
TLDR; Change your Java code, make the CallableStatement reference parameters by index instead of name.
After having a similar problem I updated my JDBC driver mysql-connector-java-5.1.26-bin.jar.
The error then changed from
User does not have access to metadata required to determine stored
procedure parameter types. If rights can not be granted, configure
connection with "noAccessToProcedureBodies=true" to have driver
generate parameters that represent INOUT strings irregardless of
actual parameter types.
to
No access to parameters by name when connection has been configured not to access procedure bodies
I changed my Callable Statement to reference parameters by index instead of name, and hey presto it works.
Updating the driver may not be necessary, just knowing to use indexes instead of names when you don't have metadata access or routine body access.
Good Luck

<b>admin </b> prevent himself to drop specific tables?

How can admin prevent himself to drop or truncate specific tables, because sometimes a table is accidently truncated or deleted, making view is not a good idea. if i make a trigger ,it is implemented in all tables in db. i just want to implement it in specific tables, in sql server ?
Create TRIGGER [TR_ProtectCriticalTables]
ON DATABASE
FOR
DROP_TABLE
AS
DECLARE #eventData XML,
#uname NVARCHAR(50),
#oname NVARCHAR(100),
#otext VARCHAR(MAX),
#etype NVARCHAR(100),
#edate DATETIME
SET #eventData = eventdata()
SELECT
#edate=GETDATE(),
#uname=#eventData.value('data(/EVENT_INSTANCE/UserName)[1]', 'SYSNAME'),
#oname=#eventData.value('data(/EVENT_INSTANCE/ObjectName)[1]', 'SYSNAME'),
#otext=#eventData.value('data(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]',
'VARCHAR(MAX)'),
#etype=#eventData.value('data(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)')
IF #oname IN ('tbluser')-- You can give comma seperated list here
BEGIN
DECLARE #err varchar(100)
SET #err = 'Table ' + #oname + ' is super duper protected and cannot be dropped.'
RAISERROR (#err, 16, 1) ;
ROLLBACK;
END
GO
ENABLE TRIGGER [TR_ProtectCriticalTables] ON DATABASE
For disabling the truncate try this
EXEC sys.sp_cdc_enable_table
#source_schema = N'dbo',
#source_name = N'TestTable',
#role_name = NULL
GO
This may cause some other problems so please check before using.
Just remove the permissions dir that special admins. And create a second user which has the permissions for doing such dangerous jobs.
See also the GRANT and REVOKE SQL commands.
Please create user and create trigger for superadmin who has all permissions like :
CREATE TRIGGER reminder2
ON Customer
with execute as owner
AFTER DELETE
AS
truncate table Customer

How do I avoid timeouts with SqlServer full text search?

We're using SqlServer 2008. In SSMS, queries on the full text catalog might take 2-5 seconds the first time, but after that, return quite quickly.
On the other hand, running a query from via Linq2Sql will timeout.
Here's what we have:
The SQL Inline Table UDF
CREATE FUNCTION dbo.SearchArchiveFTS
(
#query nvarchar(255)
)
RETURNS #ret TABLE
(
ID NVarChar(12) NOT NULL,
snapshotDate DateTime NOT NULL,
-- about 10 more
)
AS BEGIN
declare #innerQuery nvarchar(255)
set #innerQuery = #query
insert into #ret
select ID,
snapshotDate,
-- about 10 more
from dbo.Archive a
where contains(a.*, #innerQuery)
return
Query in SSMS
select * from dbo.SearchArchiveFTS('query')
//3 seconds / 3k rows
Query in Linq2Sql
db.SearchArchiveFTS("query").ToArray();
// timeout exception
Any ideas on what the problem might be?
Check that your connection is not coming in with arithabort off. In SSMS it is ON
you can easily check like this
select arithabort,*
from sys.dm_exec_sessions
where is_user_process =1
just find the SPID that is hitting the DB
You also try to see what happens when you do this in SSMS
SET ARITHABORT OFF
select * from dbo.SearchArchiveFTS('query')
Does it now take a lot longer?
It is also possible that you are getting a bad plan from LINQ
You can clean out the procedure cache and memory buffers by running the following command
DBCC DROPCLEANBUFFERS
DBCC FREEPROCCACHE
Just be aware that it will wipe out all the plans on the server and SQL Server will have to recreate all of those and also read in all the data from disk again.......
I agree with #SQLMenace, when somehthing runs fast in SSMS but not from the application, it is usually a connection difference.
However, why use a function for something like that?
if you must use a function, why not use a table value function like this:
CREATE FUNCTION dbo.SearchArchiveFTS
(
#query nvarchar(255)
)
RETURNS TABLE
AS RETURN
(
select ID,
snapshotDate,
-- about 10 more
from dbo.Archive a
where contains(a.*, #query)
);
The issue appears to be related to a feature of SQL Server, where the FTS indices are unloaded after a period of inactivity. A background job to keep them fresh solved the problem.

MySQL query browser procedure error code -1

I'm having a rather strange problem with MySQL. Trying to create a procedure to update some fields in the database (the code is below).
The problem is with the line that is currently commented. It seems that if no SELECT statements get executed during the procedure MySQL query browser will return an error code of "-1, error executing SQL query".
I tried the same thing in HeidiSQL and the error was "cannot return result set". So I suppose the question is do I always have to select something in the procedure, or is there some other thing I missed.
The query works fine when the comment is removed.
DELIMITER /
DROP PROCEDURE IF EXISTS updateFavourites /
CREATE PROCEDURE updateFavourites(quota INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE artist_id,releases INT;
DECLARE c_artist Cursor FOR
SELECT Artist.id_number,COUNT(Artist.id_number) FROM Artist
JOIN CD ON CD.is_fronted_by = Artist.id_number
GROUP BY Artist.id_number;
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000'
SET done=1;
IF quota > 0 THEN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
-- SELECT 'Great success';
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;
END /
DELIMITER ;
Here's the sql to create the tables and some data:
DROP TABLE IF EXISTS CD;
DROP TABLE IF EXISTS Artist;
CREATE TABLE Artist (
id_number INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50),
);
CREATE TABLE CD (
catalog_no INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
is_fronted_by INT UNSIGNED,
rating ENUM ('favourite','top draw','good','so-so','poor','rubbish'),
CONSTRAINT fk_CD_Artist FOREIGN KEY (is_fronted_by) REFERENCES Artist(id_number) ON UPDATE CASCADE
);
INSERT INTO Artist VALUES(11,'Artist 1');
INSERT INTO Artist VALUES(10,'Artist 2');
INSERT INTO CD VALUES (7,11, 'top draw');
INSERT INTO CD VALUES (650,11,'good');
INSERT INTO CD VALUES (651,11,'good');
INSERT INTO CD VALUES (11,10,'favourite');
Query Browser is not for running scripts, just single query.
I tried your code by moving cursor into each query (except DELIMITER) and pressing Ctrl+Enter.
It created that stored procedure without problem. (just refresh schema on the left).
If you wish creating procedure, use menu "Script"->"Create stored procedure/function".
But better forget about QueryBrowser it is not supported at all (and actunally not useful).
If you have decent hardware and plenty resources, try Workbench 5.2 otherwise use SQLyog
Googling around, there are several reports of the same error, but little information to solve the problem. There's even a bug logged at mysql.com but it appears to have been abandoned without being resolved.
There's another StackOverflow question on the same error, but it's also unresolved.
All it means is that there is no result set from the query. Looking at the source code, it appears that sometimes an error status of MYX_SQL_ERROR is set when the query has no result set. Perhaps this is not an appropriate consequence?
I notice that when I use the mysql command-line client, it yields no error for calling a proc that returns no result set.
update: I tried to revive that MySQL bug report, and provide a good test case for them. They changed the bug from "no feedback" to "verified" -- so at least they acknowledge it's a bug in Query Browser:
[11 Dec 9:18] Sveta Smirnova
Bill,
thank you for the feedback. Verified
as described.
Although most likely this only be
fixed when MySQL Query Browser
functionality is part of MySQL
workbench.
I guess the workaround is to ignore the -1 error, or to test your stored procedures in the command-line mysql client, where the error does not occur.
The comment supposes the issue will disappear as the Query Browser functionality becomes part of MySQL Workbench. This is supposed to happen in MySQL Workbench 5.2. I'll download this beta and give it a try.
MySQL Workbench 5.2 is in Beta, but I would assume MySQL engineering can't predict when the Beta will become GA. Those kinds of predictions are hard enough under standard conditions, but there's a lot of extra uncertainty of MySQL's fate due to the unresolved Oracle acquisition.
update: Okay, I have tried MySQL Workbench 5.2.10 beta. I executed a stored procedure like this:
CREATE PROCEDURE FooProc(doquery SMALLINT)
BEGIN
IF doquery THEN
SELECT * FROM Foo;
END IF;
END
When I CALL FooProc(0) the response is no result set, and the status is simply "OK".
When I CALL FooProc(1) the response is the result of SELECT * FROM Foo as expected.
However, there's another bug related to calling procedures. Procedures may have multiple result sets, so it's hard to know when to close the statement when you execute a CALL query. The consequence is that MySQL Workbench 5.2 doesn't close the statement, and if you try to do another query (either CALL or SELECT) it gives you an error:
Commands out of sync; you can't run this command now.
MySQL doesn't support multiple concurrent open queries. So the last one must be closed before you can start a new one. But it isn't closing the CALL query. This bug is also logged at the MySQL site.
The bug about commands out of sync has been resolved. They say it's fixed in MySQL Workbench 5.2.11.
Try putting BEGIN and END blocks around the multiple statements in the IF block as such:
IF quota > 0 THEN
BEGIN
OPEN c_artist;
REPEAT
FETCH c_artist INTO artist_id,releases;
IF NOT done THEN
IF releases >= quota THEN
UPDATE CD SET CD.rating='favourite' WHERE CD.is_fronted_by = artist_id;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE c_artist;
END;
ELSE
SELECT CONCAT('\'quota\' must be greater than 0.',' Got (',quota,')');
END IF;