ANT Builds and MS SQL - Run .sql scripts in your build.

Yet another great ANT integration feature, executing .sql scripts. One of the things I like to do during development is keep a folder of all t-sql scripts needed to get my application up and running. These normally include creating tables, views, stored procs, and indexes as well as scripts that insert default records such as lookup values. I make sure the scripts can be run on a brand new installation as well as for updating an existing one (Listing 1.1).

Listing 1
/************************************************************
/ Script: Person Table /
/ Purpose: Creates Person Table /
/ Script Date: 02/07/2007 /
/ By: Phill Nacelli - Software Architect /
/************************************************************

-- if table does not exist create it.
IF NOT EXISTS (SELECT name FROM sysobjects WHERE name = N'Person' AND type = 'U')
BEGIN

CREATE TABLE Person (
personID int IDENTITY (1, 1) NOT NULL ,
firstName varchar (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
middleName varchar (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
lastName varchar (50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
dateOfBirth datetime NULL ,
suffix varchar (30) COLLATE SQL_Latin1_General_CP1_CI_AS NULL ,
CONSTRAINT PK_Person PRIMARY KEY CLUSTERED
(
personID
) ON PRIMARY
) ON PRIMARY

END
GO

-- insert new suffix column if does not exist
IF NOT EXISTS (SELECT sc.name FROM syscolumns sc INNER JOIN sysobjects so ON sc.id = so.id WHERE so.name = 'Person'

AND so.type = 'U' AND sc.name = 'suffix')
BEGIN

ALTER TABLE Person
ADD suffix varchar(30) NULL

END

-- insert new prefix column if does not exist
IF NOT EXISTS (SELECT sc.name FROM syscolumns sc INNER JOIN sysobjects so ON sc.id = so.id WHERE so.name = 'Person'

AND so.type = 'U' AND sc.name = 'prefix')
BEGIN

ALTER TABLE Person
ADD prefix varchar(30) NULL

END

However, running multiple t-sql scripts at a time is not a lot of fun, and that's when ANT comes in. It allows you to run .sql scripts using jdbc drivers directly into your database. Here's a step by step how:

1. Make sure you have your database's JDBC .jar file. These can be found either on the database manufacture's website or via online. Sun's Developer Network (SDN) has made this even easier by creating a JDBC search engine here.

2. In your build file, create the following properties to link to your database and .sql script files (Listing 1.2):

Listing 1.2 - build.xml
<!-- directory where db scritps reside -->
<property name="sql.scriptPath" value="D:\\inetpub\\aLogix\\database" />
<!-- db server name -->
<property name="sql.serverName" value="MSSQL_DevBox" />
<!-- db name -->
<property name="sql.dbName" value="aLogix" />
<!-- jdbc url -->
<property name="sql.jdbcURL" value="jdbc:sqlserver://${sql.serverName};databasename=${sql.dbName}" />
<!-- jdbc path -->
<property name="sql.jdbcPath" value="C:\jdbcDrivers\microsoft-jdbc-2005\lib\sqljdbc.jar" />
<!-- jdbc classpath -->
<property name="sql.jdbcClasspath" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />

3. Then simply create the target implementing the sql task tag to run your scripts. Here I have created two targets, the first runs all my sql scripts, the other prompts me which .sql file I want to run (Listing 1.3):

Listing 1.3 - build.xml
<target name="sql.getLogin">
<input message="Please enter sql server username:" addproperty="sql.username" />
<input message="Please enter sql server password:" addproperty="sql.password" />
</target>

<target name="sql.runAll" depends="sql.getLogin">
<sql driver="${sql.jdbcClasspath}" url="${sql.jdbcURL}" userid="${sql.username}" password="${sql.password}">
<classpath>
<pathelement path="${sql.jdbcPath}" />
</classpath>
<transaction src="${sql.scriptPath}\tables\Person.sql" />
<transaction src="${sql.scriptPath}\tables\Company.sql" />
<transaction src="${sql.scriptPath}\tables\Address.sql" />
<transaction src="${sql.scriptPath}\tables\Country.sql" />
<transaction src="${sql.scriptPath}\tables\StateProvince.sql" />
<transaction src="${sql.scriptPath}\lookupData\Country.sql" />
<transaction src="${sql.scriptPath}\lookupData\StateProvince.sql" />
</sql>
</target>

<target name="sql.runByFileName" depends="sql.getLogin">
<input message="Please enter sql script name to be executed:" addproperty="sql.filename" />
<sql driver="${sql.jdbcClasspath}" url="${sql.jdbcURL}" userid="${sql.username}" password="${sql.password}">
<classpath>
<pathelement path="${sql.jdbcClassPath}" />
</classpath>
<transaction src="${sql.scriptPath}\tables\${sql.filename}.sql" />
</sql>
</target>

That's it! Here's another simple way how ANT can make a developer's life a lot easier. Hopefully this example can be of some use on your toolbox as it has been for me.

Related Blog Entries

Comments
Nice, I'm an ANT newbie and just mastered getting a build from VSS/SVN.

Very timely post for me! Cheers
# Posted By dc | 2/9/07 3:50 AM
First, thanks for sharing the code.

I tried your code using Eclipse/Ant against MS SQL 2005. The sample SQL script failed to run and errored due to syntax. The script ran fine in Query Analyzer. I modified the SQL scipt to take out the word Go and added ";" at end of each block of sql. Below is the modified /worked script

-- Script: Person Table /
-- Purpose: Creates Person Table /
-- Script Date: 02/07/2007 /
-- By: Phill Nacelli - Software Architect /

-- if table does not exist create it.
IF NOT EXISTS (SELECT name FROM sysobjects WHERE name = N'Person' AND type = 'U')
BEGIN

CREATE TABLE [dbo].[Person](
   [personID] [int] IDENTITY(1,1) NOT NULL,
   [firstName] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
   [middleName] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
   [lastName] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
   [dateOfBirth] [datetime] NULL,
   [suffix] [varchar](30) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
   [prefix] [varchar](30) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED
(
   [personID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

END;


-- insert new suffix column if does not exist
IF NOT EXISTS (SELECT sc.name FROM syscolumns sc INNER JOIN sysobjects so ON sc.id = so.id WHERE so.name = 'Person'

AND so.type = 'U' AND sc.name = 'suffix')
BEGIN

ALTER TABLE Person
ADD suffix varchar(30) NULL

END;

-- insert new prefix column if does not exist
IF NOT EXISTS (SELECT sc.name FROM syscolumns sc INNER JOIN sysobjects so ON sc.id = so.id WHERE so.name = 'Person'

AND so.type = 'U' AND sc.name = 'prefix')
BEGIN

ALTER TABLE Person
ADD prefix varchar(30) NULL

END
# Posted By Fuji | 4/9/07 6:37 PM
@Fuji

An alternative fix is to leave 'GO' in your SQL statements and add delimiter='GO' as an attribute in the ant sql task:

<pre>
<sql driver="com.SomeDriver" url="jdbc:someUrl" userid="someuser" password="somepassword" classpathref="myclasspath" delimiter="GO">
<transaction src="create_db.sql"/>
</sql>
</pre>

Regards,
Eliot
# Posted By Eliot Sykes | 5/9/07 2:03 AM
Hi Phill, thanks for the article. One short question. I try to run a Create Database command in ANT and get this error: "CREATE DATABASE statement not allowed within multi-statement transaction."

Do you know if it is at all possible to run Create Database commands within a ANT script?

(I'm using MS SQL 2000, and a MS JDBC driver)
# Posted By Trond Ulseth | 8/7/07 1:09 AM
Hi..Thanks for the examples and description..I have a question though..

Cant i add all the sql scripts in one and call the single sql script.

a sample would be
<property name="sql.file" location="${PLSQL_ROOT}/DropTables.sql"/>

src="${sql.file}"

Now DropTables.sql will have calls to other sql scripts like

@CreateTables
@RunMigration

This isnt working , as i want it to..is there a way to accomplish this ?

Thanks,
Sumit
# Posted By Sumit | 3/12/08 12:02 PM
The example works, everything is Ok now. Though I really nad problems with SQL and had to learn for quite a long tims (downloaded much info by http://rapid4me.com SE) and at last started to understand something.
# Posted By pesrter | 9/15/09 6:04 AM
Great site i needed the rar password though, i found it http://filepasswords.com
# Posted By Bobby | 10/25/09 6:54 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.003. Powered by ColdFusion Server v8,0,1,195765.