Click here to Skip to main content
Click here to Skip to main content

Web SQL Utility

By , 30 Aug 2006
 

Screenshot

Introduction

This little ASP.NET utility lets you run SQL queries against databases accessible to the web server. Initially, this started as a script to run queries against an Access database, without having to download it. Every now and then, you might need to do maintenance chores, or retrieve some raw data to check something. This utility facilitates that by allowing queries to be saved as a favourite, as well as keeping the history of previous queries.

Features

  • Configure multiple connections (OLEDB, SQL Server, ODBC)
  • Display multiple result grids
  • Store query history (remembers target database)
  • Save favourite queries
  • Cross-browser compatible
  • View schema for database, with clickable column names (replace selection in query textbox)
  • Generate INSERT queries
  • Download DataSet XML
  • Download CSV

Details

This utility is not meant to replace database admin tools like phpMyAdmin. Rather, it was built to be simple and light-weight, allowing it to be integrated into other web applications. It's meant to provide quick and easy access to your data, and allow you to test queries, as well as modify your databases. You can configure multiple connections (not necessarily of the same database type), which gives you a common interface regardless of which database you need to query. The history and favourites-list remembers which database a query was executed against, and will select the appropriate radio button when you click on a query in those lists.

It also generates schema details for OLEDB, SQL Server, and MySQL connections. In the schema popup, you can click on the column names or table name headings to insert the name into the query textbox (replacing the current selection). The schema popup does not reload when you click the Schema link if that schema is already open in the popup. The popup will merely get focused to the forefront.

Screenshot

The Execute button, in addition to the obvious, adds the query to the history, and if the "Add to favourites" checkbox is checked, adds the query to the favourites. The shortcut key for this button is Alt-E. Multiple queries can be submitted simultaneously - separated by GO statements or ";;;" (customizable). Multiple result grids will be displayed. Note: the user-account that aspnet_wp runs as will need Modify permission on the configured paths for favourites and history (normally, ASPNET or INTERACTIVE_NETWORK).

The INSERTs button will generate INSERT SQL queries that you can execute against another database (e.g., for backing up a single table). If you use this feature with SQL Server, note that you should specify that you are overriding IDENTITY values, by running the following queries before and after the INSERT queries:

SET IDENTITY_INSERT table_name ON

--- INSERT QUERIES ---

SET IDENTITY_INSERT table_name OFF

The DataSet XML button will let you download the DataSet XML for your SELECT queries. Multiple result sets are supported. The tables are named Table1, Table2, Table3 etc., for each result set.

The CSV button will let you download a single CSV, with each result set underneath the other.

The HTML encode results checkbox escapes any HTML characters in your result set.

The Wrap query textbox checkbox changes the text-wrapping on the query textbox (i.e., horizontal scrollbar on/off for long lines). This was a major headache to implement for Firefox (v1.5) - which required coming up with a complete workaround since setting the wrap attribute on the TEXTAREA element just didn't have any effect. I assume this is a bug in Firefox, and is the first time I feel let down by this impressive browser. (More details on the workaround below.)

Initially, the SQL page shows the Favourites "tab" selected. But once a query is executed, the History "tab" becomes selected by default. They're not really tabs, visually, but the bold text shows which list is selected. The History list has a "Complete History" link underneath which will be shown queries executed for previous sessions in a popup. This is useful to keep a record of queries run by yourself or anybody else. If you suddenly have 1000 rows missing from your database, you can investigate the query history for the potential cause. The popup has delete links to clear a particular history session, or all of them.

Configuration

There is a custom configuration section that lets you customise this utility. Here is an example:

<DbUtilConfig>
   <Connections>
      <Connection name="Access" type="oledb" cs="" csAppKey="AccessDb" />
      <Connection name="SQL Server" type="mssql" cs="" 
                  csConfKey="SqlServerConnStringIAM" />
      <Connection name="MySQL" type="odbc" 
                  cs="DRIVER={MySQL ODBC 3.51 Driver}; SERVER=localhost;
                      USER=test; PASSWORD=test; DATABASE=test" />
   </Connections>
   <FavouritesFile>data/SqlFavourites.xml</FavouritesFile>
   <HistoryFolder>data/SqlHistory</HistoryFolder>
   <QuerySeparator>;;;</QuerySeparator>
   <ResultGridColors Border="#777" HeaderBg="#ffc" 
                     Header="#000" ItemBg="#fff" AltItemBg="#eee" />
</DbUtilConfig>

Connection

  • The name attribute is the label displayed in the RadioButtonList control. This is also the key that is associated with history queries (so they remember which database they were executed against).
  • The type attribute can have three possible values: oledb, mssql, odbc (case insensitive).
  • The cs attribute specifies the connection string.
  • If your connection string is already defined in Configuration.AppSettings, you can specify the key name in the csConfKey attribute.
  • If your connection string is already defined in the Application variable, you can specify the key name in the csAppKey attribute.

FavouritesFile

Path to the XML file where favourite queries are stored.

HistoryFolder

Path to the directory where history XML files are stored.

QuerySeparator

This is the string used to delimit multiple queries. When executing, if there are any occurrences of this string, the query will be split with this string. If there are no occurrences of this string, the code will attempt to split the query with GO statements (e.g., for SQL Server scripts).

ResultGridColors

Lets you change the colour scheme of the result DataGrids. (By the way, #fff is equivalent to #ffffff).

  • Border attribute - DataGrid border colour
  • HeaderBg attribute - DataGrid header background colour
  • Header attribute - DataGrid header text colour
  • ItemBg / AltItemBg attributes - DataGrid item / alternating item background colour

Code

Snippet 1 (JavaScript)

The JavaScript code to replace the selection in the TEXTAREA with the column name is a fair bit simpler in Internet Explorer compared to that in Firefox:

if (document.all) // IE
   window.opener.document.selection.createRange().text = o.innerHTML
else // FF
{
   var selStart = queryTextArea.selectionStart
   queryTextArea.value = 
      queryTextArea.value.substring(0, selStart) + o.innerHTML +
      queryTextArea.value.substring(queryTextArea.selectionEnd)
   queryTextArea.selectionStart = selStart + o.innerHTML.length
   queryTextArea.selectionEnd = queryTextArea.selectionStart
}

As you can see, Firefox gives you access to the selection positions, not the selection itself. So you have to use string manipulation to create the new value. Then, I set the selection start/end to select the string that was just inserted.

Snippet 2 (C#)

Where possible, the System.Data interfaces were used, to support different database types. But in some places, the object had to be cast to its actual provider-specific type. Here is an example:

IDbConnection conn = conf.GetConnection(RblWhichDb.SelectedValue);
cmd = conn.CreateCommand();
cmd.CommandText = query;
conn.Open();

try
{
   IDataReader reader = cmd.ExecuteReader();

   bool hasRows = false;
   if (conn is OleDbConnection)
      hasRows = ((OleDbDataReader)reader).HasRows;
   else if (conn is SqlConnection)
      hasRows = ((SqlDataReader)reader).HasRows;
   else if (conn is OdbcConnection)
      hasRows = ((OdbcDataReader)reader).HasRows;

Snippet 3 (JavaScript)

As I mentioned earlier, a workaround was required for Firefox to toggle the wrapping of the query TEXTAREA. The strange thing is that Firefox does support the wrap attribute when rendering the page, but has absolutely no effect when setting this attribute with JavaScript.

This HTML gives you a TEXTAREA with no text wrapping (i.e., horizontal scrollbar for long lines):

<textarea wrap="false" rows="7" cols="80" id="TxtQuery">

But this JavaScript has no effect on the wrapping:

document.getElementById("TxtQuery").wrap = true

Nor does this:

document.getElementById("TxtQuery").setAttribute("wrap", "true")

I found this really peculiar, but I didn't give up there. I came up with a workaround to re-create the HTML, and assign it as the innerHTML of the parent element:

function toggleWrap(checked)
{
   if (window.navigator.userAgent.indexOf("Gecko") != -1)
   {
      // workaround for Firefox because setting
      // 'wrap' attribute just doesn't work :(
      var val = document.getElementById("TxtQuery").value
      var html = document.getElementById("TxtQuery").parentNode.innerHTML
      html = html.replace('wrap="off"', '')
      if (!checked)
         html = html.substring(0, html.indexOf('<textarea') + 9) + 
                ' wrap="off" ' +
                html.substring(html.indexOf('<textarea') + 9)
      document.getElementById("TxtQuery").parentNode.innerHTML = html
      document.getElementById("TxtQuery").value = val
   }
   else
      document.getElementById('TxtQuery').wrap = (checked) ? 'soft' : 'off'
}

It first gets the value of the TEXTAREA. It then retrieves the innerHTML string from the parent element, adds or removes the string "wrap="off"" from the TEXTAREA element, and then assigns it back as the innerHTML of the parent element. It then sets the value of the TEXTAREA. Nothing too involved... but compared to the single line for Internet Explorer, a bit of a headache.

By the way, I'm experimenting with omitting the optional semi-colons in my JavaScript code.

Snippet 4 (C#)

This ASP.NET application uses a custom configuration section in Web.config. You've probably encountered this before - it lets you put your own custom section in Web.config. All custom sections have to be declared at the top of Web.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <configSections>
   <section name="DbUtilConfig" 
            type="WebSqlUtility.ConfigHandler, WebSqlUtility" />
 </configSections>

The name attribute indicates the element name of the custom section. The type attribute indicates your .NET class to handle the custom section - i.e., "[namespace.classname], [assembly name]".

A simple configuration handler class would look like this:

public class ConfigHandler : IConfigurationSectionHandler
{
   public object Create(object parent, object configContext, 
                                       XmlNode sectionNode)
   {
      return sectionNode.ChildNodes[0].InnerText;
   }
}

Then you would access the value in your application as follows:

string val = (string)ConfigurationSettings.GetConfig("Blah");

where "Blah" would be the section name as defined in configSections.

Here is an excerpt from the configuration handler class for the DbUtilConfig custom section in this application:

XmlNodeList nodes = sectionNode.SelectNodes("Connections/Connection");
foreach (XmlNode node in nodes)
{
 string name = node.Attributes["name"].Value;
 string type = node.Attributes["type"].Value;
 string cs = null;
 if (node.Attributes["csAppKey"] != null)
    cs = (string)
          HttpContext.Current.Application[node.Attributes["csAppKey"].Value];
 else if (node.Attributes["csConfKey"] != null)
    cs = ConfigurationSettings.AppSettings[node.Attributes["csConfKey"].Value];
 else
    cs = node.Attributes["cs"].Value;

 if (type == "oledb")
    arrConnections.Add(new OleDbConnection(cs));
 else if (type == "mssql")
    arrConnections.Add(new SqlConnection(cs));
 else if (type == "odbc")
    arrConnections.Add(new OdbcConnection(cs));

 arrNames.Add(name);
}

Important notes

  • Never deploy this without restricting access to authorized users. This app provides full access to your databases, so obviously you need to lock it down. The web.config included in the ZIP files denies anonymous users.
  • The INSERT queries generated by the INSERTs button have not been tested with image column types. This scenario, and possibly others, might cause issues and need improving.
  • If you want to prevent download access to the history and favourites XML files, you'll need to disable Directory Browsing and deny IUSR_xx access on the history folder and favourites file.

Future ideas

  • Ability to add a note/description for a favourite.
  • Button to toggle ScrollingGrid^ for a result grid.
  • Ability to enlarge query textbox.
  • Generate queries option to exclude primary key.
  • Compress download XML/CSV as a ZIP file.

I'm not sure what kind of priority I can give to the future development of this app, but feel free to post any other suggestions and ideas below.

Conclusion

This has been yet another interesting adventure in solving browser issues and achieving common behaviour across the two major browsers (IE, Firefox). I hope either all or parts of this app are useful to others.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Ashley van Gerven
Australia Australia
Member
Ash is a C# developer (MCAD) with a background developing e-commerce and content management solutions. His current role includes working with VOIP systems, integration and maintenance of business and billing apps. His personal projects include the ScrollingGrid web control to enable cross-browser freeze-header 2-way scrolling of DataGrids. His other interests include travel, cinema, Squash, photography, Muay Thai.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralA great utility!memberSjackson20829 Dec '10 - 23:55 
Many thanks for this, works a treat for me! Big Grin | :-D
For some reason, on my production server, the list of connections gets repeated, so I added a check to see if the label already exists in the radio button list.
Don't have debugging enabled in production so unsure why. Any thoughts?
GeneralPerfectmemberRiot_starter27 Aug '08 - 3:25 
Exactly what I needed. Thanks!
Generalerror running with ASP.NET 2.0 [modified]memberitinko26 Feb '07 - 4:52 
I loaded this project in Visual Web Developer 2005 Express and after converting when I try and run the project I get this error:
 
"DbUtilConfig: Could not load file or assembly 'WebSqlUtility' or one of its dependencies. The system cannot find the file specified. (C:\Documents and Settings\mla\My Documents\My Webs\WebSqlUtility\web.config line 4)"
 
Line 4 from web.config:
< section name="DbUtilConfig" type="WebSqlUtility.ConfigHandler, WebSqlUtility"/ >
 
I'm not sure of the cause, as I don't have VS 2005 I can't rebuild the assembly. Is there a way to use this without creating the WebSqlUtility assembly?
 
It looks like a great tool, Thanks!
 

 

-- modified at 14:44 Monday 26th February, 2007
QuestionDoesn't seem to support MS SQL "UNIQUEIDENTIFIER" columnsmemberMarc Scheuner22 Jan '07 - 6:16 
It seems as if the fairly common columns of type "UNIQUEIDENTIFIER" (or GUID) in MS SQL Server aren't supported - even if I have them listed in my SQL query, they don't show up in the result grid.
 
Yes, of course - I can cast them to e.g. VARCHAR(50) - but if you have dozens of those, it gets a bit tiring and annoying.
 
Any chance you could add support so that UNIQUEIDENTIFIER column would be supported and shown in the result grid "out-of-the-box" ?
 
Thanks!
 
=============================
Marc Scheuner, Berne, Switzerland
mscheuner - at - gmail.com
May The Source Be With You!
AnswerRe: Doesn't seem to support MS SQL "UNIQUEIDENTIFIER" columnsmemberAshley van Gerven22 Jan '07 - 12:05 
That's something I'd like to look into at some stage. I don't really use that column type so have never encountered this issue. Thanks for the feedback.
 
"For fifty bucks I'd put my face in their soup and blow." - George Costanza
 CP article: SmartPager - a Flickr-style pager control with go-to-page popup layer.
Questionabout loading your module into DotNetNukememberrockstarbill6 Sep '06 - 8:24 
Hi,
Have been taking a look at your work on the SQl utility and think it is great. Would like to use it on our DotNetNuke framework extranet. Some of the form info is getting too big, and I need to run queries to select and delete some of the old records. I think this would work fine, instead of through Enterprise Manager in SQL 2000. My programming knowledge is limited, that is why I use DotNetNuke! which is a fantastic method to get something like this up. I take it to introduce your product, I would need to add the web.config info to the original web.config file, then add the dll file to the bin folder, configure the sql portion in the config file to point at the necessary db, and put the rest in the Providers folder. Not sure how this would configure with DNN? or if possible.
Hope you can help on this,
 
Thanks,
 
Kevin Graves
AnswerRe: about loading your module into DotNetNukememberAshley van Gerven6 Sep '06 - 8:49 
I don't know too much about DNN, but you would need to:
- copy the DLL to bin
- copy .aspx files + script folder to password-protected folder
- edit DNN web.config to include relevant sections from this web.config

 
"For fifty bucks I'd put my face in their soup and blow." - George Costanza
  ~ Web SQL Utility - query Access, SQL server, MySQL. Stores history, favourites.

AnswerRe: about loading your module into DotNetNukememberzitniet7 Sep '06 - 2:08 
Hi Kevin,
 
Try the free DNN training videos at
http://dotnetnuke.com/About/BooksandTrainingVideos/tabid/810/Default.aspx[^]
 
I'm sure you will get the picture of how to incorporate your .NET code into DNN.
 
A Module is loaded dynamically into a container (skinable module holder) which in turn is dynamically injected into the skin (skinable page structure) which in turn is dynamically injected into the Default.aspx page.
To turn your ASP.NET code into a DNN module you need to inherit from the PotalModuleBase Class that acts as the module plug-in (The interface between the DNN core and your ASP.NET code).
In DNN you will have to add module definitions to let DNN know where the different page types for this module can be found and you as admin/host can choose from a list of installed modules and place it somewhere on a DNN page. The moment the page you request the page the whole show starts as above.
 
If data from a database is involved in your module development then you will have to look into the DataProvider mechanisme too, if you want to follow the best practices.
 
There is a whole lot more stuff to tell about DNN and to use in your development. There are books about DNN and if you search for DNN modules, skins & hosting on Google you won't get borred during this year winter...
 
Succes soldier!
 
Zit.
 


 
Just give me the blue pill...i dont want to go down into rabbitholes... - Alice.

GeneralNice Jobmemberrodrigo diniz31 Aug '06 - 2:10 
I am begining to develop something very similar...in fact I started the developement monday.
My app is a queryanaliser clone.For now is just a demo in very early stages.
 
http://www.rodrigodiniz.qsh.eu/queryanaliser.aspx
 
Rodrigo Diniz

Microsoft Certifed Professional
http://www.rodrigodiniz.qsh.eu/

GeneralRe: Nice JobmemberAshley van Gerven31 Aug '06 - 2:28 
Cool. At one point I was toying with the idea of a another tab and displaying the schema as a treeview, but left it as a simple popup in the end.
 
"Nothing ever changes by staying the same." - David Brent (BBC's The Office)
 ~ ScrollingGrid: A cross-browser freeze-header control for the ASP.NET DataGrid

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130523.1 | Last Updated 30 Aug 2006
Article Copyright 2006 by Ashley van Gerven
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid