adl/transforms/adl2mssql.xslt
2008-01-21 16:38:31 +00:00

472 lines
20 KiB
HTML
Executable file

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
C1873 SRU Hospitality
adl2mssql.xsl
(c) 2007 Cygnet Solutions Ltd
Convert ADL to MS-SQL
$Author: af $
$Revision: 1.1 $
-->
<xsl:output indent="no" encoding="utf-8" method="text"/>
<xsl:template match="application">
-------------------------------------------------------------------------------------------------
--
-- Database for application <xsl:value-of select="@name"/> version <xsl:value-of select="@version"/>
-- Generated for MS-SQL 2000+ using adl2mssql.xsl $Revision: 1.1 $
--
-- Code generator (c) 2007 Cygnet Solutions Ltd
--
-------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------
-- authentication roles
-------------------------------------------------------------------------------------------------
<xsl:apply-templates select="group"/>
-------------------------------------------------------------------------------------------------
-- tables, views and permissions
-------------------------------------------------------------------------------------------------
<xsl:apply-templates select="entity" mode="table"/>
<xsl:apply-templates select="entity" mode="view"/>
-------------------------------------------------------------------------------------------------
-- referential integrity constraints
-------------------------------------------------------------------------------------------------
<xsl:for-each select="entity">
<xsl:variable name="nearside" select="@name"/>
<xsl:for-each select="property[@type='entity']">
<xsl:call-template name="referentialintegrity">
<xsl:with-param name="nearside" select="$nearside"/>
</xsl:call-template>
</xsl:for-each>
</xsl:for-each>
-------------------------------------------------------------------------------------------------
-- end of file
-------------------------------------------------------------------------------------------------
</xsl:template>
<xsl:template match="group">
execute sp_addrole @rolename = '<xsl:value-of select="@name"/>'
GO
</xsl:template>
<xsl:template name="referentialintegrity">
<xsl:param name="nearside"/>
<!-- set up referential integrity constraints for primary tables -->
ALTER TABLE "<xsl:value-of select="$nearside"/>"
ADD FOREIGN KEY ( "<xsl:value-of select="@name"/>")
REFERENCES "<xsl:value-of select="@entity"/>" ON DELETE NO ACTION
GO
</xsl:template>
<xsl:template match="entity" mode="table">
<xsl:variable name="table" select="@name"/>
-------------------------------------------------------------------------------------------------
-- primary table <xsl:value-of select="@name"/>
-------------------------------------------------------------------------------------------------
CREATE TABLE "<xsl:value-of select="@name"/>"
(
<xsl:apply-templates select="property[@type!='link']"/>
<xsl:value-of select="@name"/>Id INT IDENTITY( 1, 1) PRIMARY KEY
)
GO
---- permissions ------------------------------------------------------------------------------
<xsl:for-each select="permission">
<xsl:call-template name="permission">
<xsl:with-param name="table" select="$table"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template match="entity" mode="view">
<xsl:variable name="table" select="@name"/>
-------------------------------------------------------------------------------------------------
-- convenience view VW_DL_<xsl:value-of select="@name"/> for default list
-------------------------------------------------------------------------------------------------
CREATE VIEW "VW_DL_<xsl:value-of select="@name"/>" AS
SELECT "<xsl:value-of select="@name"/>"."<xsl:value-of select="@name"/>Id",
<xsl:for-each select="property[@type!='link']">
<xsl:choose>
<xsl:when test="@type='entity'">
<xsl:call-template name="distinctfield">
<xsl:with-param name="table" select="@entity"/>
<xsl:with-param name="alias" select="@name"/>
</xsl:call-template> AS <xsl:value-of select="@name"/></xsl:when>
<xsl:otherwise>"<xsl:value-of select="$table"/>"."<xsl:value-of select="@name"/>"</xsl:otherwise>
</xsl:choose><xsl:choose>
<xsl:when test="position() = last()"></xsl:when>
<xsl:otherwise>,
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
FROM "<xsl:value-of select="@name"/>" <xsl:for-each
select="property[@type='entity']">, "<xsl:value-of select="@entity"/>" AS "<xsl:value-of select="@name"/>"</xsl:for-each>
<xsl:text>
</xsl:text>
<xsl:for-each select="property[@type='entity']">
<xsl:choose>
<xsl:when test="position() = 1">WHERE </xsl:when>
<xsl:otherwise>AND </xsl:otherwise>
</xsl:choose>"<xsl:value-of select="$table"/>"."<xsl:value-of
select="@name"/>" = "<xsl:value-of select="@name"/>"."<xsl:value-of select="@entity"/>Id"
</xsl:for-each>
GO
---- permissions ------------------------------------------------------------------------------
<xsl:for-each select="permission">
<xsl:call-template name="viewpermission">
<xsl:with-param name="table" select="$table"/>
</xsl:call-template>
</xsl:for-each>
<!-- link tables -->
<xsl:for-each select="property[@type='link']">
<xsl:call-template name="linktable">
<xsl:with-param name="nearside" select="$table"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="distinctfield">
<xsl:param name="table"/>
<xsl:param name="alias"/>
<!--
print the names of the distinguishing fields in this table,
concatenating into a single string.
-->
<xsl:for-each select="/application/entity[@name=$table]">
<xsl:for-each select="property[@distinct='user' or @distinct='all']">
<xsl:choose>
<xsl:when test="@type='entity'">
<xsl:call-template name="distinctfield">
<xsl:with-param name="table" select="@entity"/>
<xsl:with-param name="alias" select="concat( $alias, '_', @name)"></xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
"<xsl:value-of select="$alias"/>"."<xsl:value-of
select="@name"/>"<xsl:if test="position() != last()"> + ' ' + </xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template name="permission">
<xsl:param name="table"/>
<!-- decode the permissions for a table -->
<xsl:choose>
<xsl:when test="@permission='read'">GRANT SELECT ON "<xsl:value-of
select="$table"/>" TO <xsl:value-of select="@group"/>
GO</xsl:when>
<xsl:when test="@permission='insert'">GRANT INSERT ON "<xsl:value-of
select="$table"/>" TO <xsl:value-of select="@group"/>
GO</xsl:when>
<xsl:when test="@permission='noedit'">GRANT SELECT, INSERT ON "<xsl:value-of
select="$table"/>" TO <xsl:value-of select="@group"/>
GO</xsl:when>
<xsl:when test="@permission='edit'">GRANT SELECT, INSERT, UPDATE ON "<xsl:value-of
select="$table"/>" TO <xsl:value-of select="@group"/>
GO</xsl:when>
<xsl:when test="@permission='all'">GRANT SELECT, INSERT, UPDATE, DELETE ON "<xsl:value-of
select="$table"/>" TO <xsl:value-of select="@group"/>
GO</xsl:when>
<xsl:otherwise>REVOKE ALL ON "<xsl:value-of
select="$table"/>" FROM <xsl:value-of select="@group"/>
GO</xsl:otherwise>
</xsl:choose>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="viewpermission">
<xsl:param name="table"/>
<!-- decode the permissions for a convenience view -->
<xsl:choose>
<xsl:when test="@permission='none'">REVOKE ALL ON "VW_DL_<xsl:value-of
select="$table"/>" FROM <xsl:value-of select="@group"/>
GO</xsl:when>
<xsl:when test="@permission='insert'">REVOKE ALL ON "VW_DL_<xsl:value-of
select="$table"/>" FROM <xsl:value-of select="@group"/>
GO</xsl:when>
<xsl:otherwise>GRANT SELECT ON "VW_DL_<xsl:value-of
select="$table"/>" TO <xsl:value-of select="@group"/>
GO</xsl:otherwise>
</xsl:choose>
<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template name="linktable">
<xsl:param name="nearside"/>
<!-- This is tricky. For any many-to-many relationship between two
entities, we only want to create one link table, even if (as should be)
a property of type 'link' has been declared at both ends -->
<xsl:variable name="farside">
<xsl:choose>
<xsl:when test="@entity = $nearside">
<xsl:value-of select="concat( @entity, '_1')"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@entity"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="comparison">
<xsl:call-template name="stringcompare">
<xsl:with-param name="node1" select="$nearside"/>
<xsl:with-param name="node2" select="@entity"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="farentity" select="/application/entity[@name=$farside]"/>
-- Problems with responsibility for generating link tables:
-- @entity = <xsl:value-of select="@entity"/>
-- $nearside = <xsl:value-of select="$nearside"/>
-- $farside = <xsl:value-of select="$farside"/>
-- $farentity = <xsl:value-of select="count( $farentity/property)"/>
-- farlink = <xsl:value-of select="$farentity/property[@type='link' and @entity=$nearside]/@name"/>
-- comparison = '<xsl:value-of select="$comparison"/>'
<xsl:variable name="myresponsibility">
<xsl:choose>
<!-- if we could use the compare( string, string) function this would be a lot simpler, but
unfortunately that's in XSL 2.0, and neither NAnt nor Visual Studio can manage that -->
<!-- if the link is back to me, then obviously I'm responsible -->
<xsl:when test="$comparison = 0">true</xsl:when>
<!-- generally, the entity whose name is later in the default collating sequence
shall not be responsible. -->
<xsl:when test="$comparison = -1">true</xsl:when>
<!-- However if the one that is earlier doesn't have a 'link'
property for this join, however, then later end will have to do it -->
<xsl:when test="$comparison = 1">
<xsl:choose>
<!-- the far side is doing it... -->
<xsl:when test="$farentity/property[@type='link' and @entity=$nearside]">false</xsl:when>
<xsl:otherwise>true</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="tablename">
<xsl:choose>
<xsl:when test="$comparison =-1">
<xsl:value-of select="concat( 'ln_', $nearside, '_', @entity)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat( 'ln_', @entity, '_', $nearside)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
-- Responsibility = '<xsl:value-of select="$myresponsibility"/>'
<xsl:choose>
<xsl:when test="$myresponsibility='true'">
<!-- create a linking table -->
-------------------------------------------------------------------------------------------------
-- link table joining <xsl:value-of select="$nearside"/> with <xsl:value-of select="@entity"/>
-------------------------------------------------------------------------------------------------
CREATE TABLE "<xsl:value-of select="$tablename"/>"
(
"<xsl:value-of select="$nearside"/>Id" INT NOT NULL,
"<xsl:value-of select="$farside"/>Id" INT NOT NULL,
)
GO
<xsl:text>
</xsl:text>
---- permissions ------------------------------------------------------------------------------
<xsl:for-each select="../permission">
<xsl:call-template name="permission">
<xsl:with-param name="table" select="$tablename"/>
</xsl:call-template>
</xsl:for-each>
<xsl:text>
</xsl:text>
---- referential integrity --------------------------------------------------------------------
<xsl:choose>
<xsl:when test="$nearside=@entity">
ALTER TABLE "<xsl:value-of select="$tablename"/>"
ADD FOREIGN KEY ( "<xsl:value-of select="$nearside"/>Id")
REFERENCES "<xsl:value-of select="$nearside"/>" ON DELETE NO ACTION
GO
ALTER TABLE "<xsl:value-of select="$tablename"/>"
ADD FOREIGN KEY ( "<xsl:value-of select="$farside"/>Id")
REFERENCES "<xsl:value-of select="$nearside"/>" ON DELETE NO ACTION
GO
</xsl:when>
<xsl:otherwise>
ALTER TABLE "<xsl:value-of select="$tablename"/>"
ADD FOREIGN KEY ( "<xsl:value-of select="$nearside"/>Id")
REFERENCES "<xsl:value-of select="$nearside"/>" ON DELETE CASCADE
GO
ALTER TABLE "<xsl:value-of select="$tablename"/>"
ADD FOREIGN KEY ( "<xsl:value-of select="@entity"/>Id")
REFERENCES "<xsl:value-of select="@entity"/>" ON DELETE CASCADE
GO
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
-- Suppressing generation of <xsl:value-of select="$tablename"/>, as it is not my responsibility
</xsl:otherwise>
</xsl:choose>
<xsl:if test="myresponsibility='true'">
</xsl:if>
</xsl:template>
<xsl:template match="property[@type='list']">
-- Suppressing output of property <xsl:value-of select="@name"/>,
-- as it is the 'one' end of a one-to-many relationship
</xsl:template>
<xsl:template match="property[@type='entity']">
"<xsl:value-of select="@name"/>" INT<xsl:if
test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if
test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="property[@type='defined']">
<xsl:variable name="name"><xsl:value-of select="@definition"/></xsl:variable>
<xsl:variable name="definitiontype"><xsl:value-of select="/application/definition[@name=$name]/@type"/></xsl:variable>
"<xsl:value-of select="@name"/>"<xsl:text> </xsl:text><xsl:choose>
<xsl:when test="$definitiontype='string'">VARCHAR( <xsl:value-of
select="/application/definition[@name=$name]/@size"/>)</xsl:when>
<xsl:when test="$definitiontype='integer'">INT</xsl:when>
<xsl:when test="$definitiontype='real'">DOUBLE PRECISION</xsl:when>
<xsl:otherwise><xsl:value-of select="$definitiontype"/></xsl:otherwise>
</xsl:choose><xsl:if
test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if
test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="property[@type='boolean']">
-- SQL Server doesn't have proper booleans!
"<xsl:value-of select="@name"/>" BIT<xsl:choose>
<xsl:when test="@default='true'"> DEFAULT 1</xsl:when>
<xsl:when test="@default='false'"> DEFAULT 0</xsl:when>
</xsl:choose><xsl:if test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="property[@type='string']">
"<xsl:value-of select="@name"/>" VARCHAR( <xsl:value-of select="@size"/>)<xsl:if
test="string(@default)"> DEFAULT '<xsl:value-of select="@default"/>'</xsl:if><xsl:if
test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="property[@type='date' or @type = 'time']">
"<xsl:value-of select="@name"/>" DATETIME<xsl:if
test="string(@default)"> DEFAULT <xsl:value-of select="@default"/>
</xsl:if><xsl:if
test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="property[@type='integer']">
"<xsl:value-of select="@name"/>" INT<xsl:if
test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if
test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="property[@type='real']">
"<xsl:value-of select="@name"/>" DOUBLE PRECISION<xsl:if
test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if
test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<xsl:template match="property">
"<xsl:value-of select="@name"/>" <xsl:text> </xsl:text><xsl:value-of select="@type"/><xsl:if
test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:if
test="@required='true'"> NOT NULL</xsl:if>,<xsl:text>
</xsl:text>
</xsl:template>
<!-- horrible, horrible hackery. Compare two strings and return
* 0 if they are identical,
* -1 if the first is earlier in the default collating sequence,
* 1 if the first is later.
In XSL 2.0 this could be done using the compare(string, string) function. -->
<xsl:template name="stringcompare">
<xsl:param name="node1"/>
<xsl:param name="node2"/>
<xsl:choose>
<xsl:when test="string($node1)=string($node2)">
<xsl:text>0</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="$node1 | $node2">
<xsl:sort select="."/>
<xsl:if test="position()=1">
<xsl:choose>
<xsl:when test="string(.) = string($node1)">
<xsl:text>-1</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>1</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>