<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns="http://bowyer.journeyman.cc/adl/1.4.1/" xmlns:adl="http://bowyer.journeyman.cc/adl/1.4.1/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --> <!-- --> <!-- adl2psql.xsl --> <!-- --> <!-- Purpose: --> <!-- XSL stylesheet to generate Postgresql [7|8] from ADL. --> <!-- --> <!-- Author: Simon Brooke <simon@weft.co.uk> --> <!-- Created: 24th January 2006 --> <!-- Copyright: (c) 2006 Simon Brooke. --> <!-- --> <!-- This file is presently not up to date with changes in ADL --> <!-- --> <!-- :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: --> <!-- JACQUARD 2 APPLICATION DESCRIPTION LANGUAGE FRAMEWORK $Revision: 1.3 $ NOTES: Needless to say this is all hugely experimental. Running the primary key field last is a hack which gets around the fact that otherwise it's extremely complex to lose the comma after the last field. Ideally where there is one 'distinct="system"' property of an entity that should be the primary key and perhaps we'll achieve that in the long run... Still to do: References in convenience views for fields which have their reference value at two removes (i.e. the 'distinguish' mechanism in ADL --> <xsl:include href="base-type-include.xslt"/> <!-- The convention to use for naming auto-generated abstract primary keys. Known values are Id - the autogenerated primary key, if any, is called just 'Id' Name - the autogenerated primary key has the same name as the entity NameId - the name of the auto generated primary key is the name of the entity followed by 'Id' Name_Id - the name of the auto generated primary key is the name of the entity followed by '_Id' --> <xsl:param name="abstract-key-name-convention" select="Id"/> <!-- the convention to use for fieldnames in link tables: Name - the name of the foreign key is the same as the name of the table linked to NameId - the name of the foreign key is the same as the name of the table linked to, followed by 'Id' Name_Id - the name of the foreign key is the same as the name of the table linked to, followed by '_Id' Name_Link - the name of the foreign key is the same as the name of the table linked to, followed by '_Link' --> <xsl:param name="linktable-field-name-convention" select="Name"/> <xsl:param name="database"/> <!-- the name and version of the product being built --> <xsl:param name="product-version" select="'Application Description Language Framework'"/> <!-- define upper and lower case letters to enable case conversion --> <xsl:variable name="ucase">ABCDEFGHIJKLMNOPQRSTUVWXYZ</xsl:variable> <xsl:variable name="lcase">abcdefghijklmnopqrstuvwxyz</xsl:variable> <!-- define SQL keywords to police these out of field names --> <xsl:variable name="sqlkeywords-multiline"> ADD EXCEPT PERCENT ALL EXEC PLAN ALTER EXECUTE PRECISION AND EXISTS PRIMARY ANY EXIT PRINT AS FETCH PROC ASC FILE PROCEDURE AUTHORIZATION FILLFACTOR PUBLIC BACKUP FOR RAISERROR BEGIN FOREIGN READ BETWEEN FREETEXT READTEXT BREAK FREETEXTTABLE RECONFIGURE BROWSE FROM REFERENCES BULK FULL REPLICATION BY FUNCTION RESTORE CASCADE GOTO RESTRICT CASE GRANT RETURN CHECK GROUP REVOKE CHECKPOINT HAVING RIGHT CLOSE HOLDLOCK ROLLBACK CLUSTERED IDENTITY ROWCOUNT COALESCE IDENTITY_INSERT ROWGUIDCOL COLLATE IDENTITYCOL RULE COLUMN IF SAVE COMMIT IN SCHEMA COMPUTE INDEX SELECT CONSTRAINT INNER SESSION_USER CONTAINS INSERT SET CONTAINSTABLE INTERSECT SETUSER CONTINUE INTO SHUTDOWN CONVERT IS SOME CREATE JOIN STATISTICS CROSS KEY SYSTEM_USER CURRENT KILL TABLE CURRENT_DATE LEFT TEXTSIZE CURRENT_TIME LIKE THEN CURRENT_TIMESTAMP LINENO TO CURRENT_USER LOAD TOP CURSOR NATIONAL TRAN DATABASE NOCHECK TRANSACTION DBCC NONCLUSTERED TRIGGER DEALLOCATE NOT TRUNCATE DECLARE NULL TSEQUAL DEFAULT NULLIF UNION DELETE OF UNIQUE DENY OFF UPDATE DESC OFFSETS UPDATETEXT DISK ON USE DISTINCT OPEN USER DISTRIBUTED OPENDATASOURCE VALUES DOUBLE OPENQUERY VARYING DROP OPENROWSET VIEW DUMMY OPENXML WAITFOR DUMP OPTION WHEN ELSE OR WHERE END ORDER WHILE ERRLVL OUTER WITH ESCAPE OVER WRITETEXT </xsl:variable> <xsl:variable name="sqlkeywords" select="concat(' ', normalize-space($sqlkeywords-multiline), ' ')"/> <xsl:template match="adl:application"> ------------------------------------------------------------------------------------------------- -- -- <xsl:value-of select="$product-version"/> -- -- Database for application <xsl:value-of select="@name"/> version <xsl:value-of select="@version"/> -- Generated for PostgreSQL [7|8] using adl2psql.xsl $Revision: 1.3 $ -- -- <xsl:value-of select="@revision"/> -- -- Code generator (c) 2006 Simon Brooke [simon@weft.co.uk] -- http://www.weft.co.uk/library/jacquard/ -- ------------------------------------------------------------------------------------------------- <xsl:if test="string-length( $database) > 0"> use "<xsl:value-of select="$database"/>"; </xsl:if> ------------------------------------------------------------------------------------------------- -- authentication roles ------------------------------------------------------------------------------------------------- <xsl:apply-templates select="adl:group"/> ------------------------------------------------------------------------------------------------- -- tables, views and permissions ------------------------------------------------------------------------------------------------- <xsl:apply-templates select="adl:entity"/> ------------------------------------------------------------------------------------------------- -- referential integrity constraints ------------------------------------------------------------------------------------------------- <xsl:for-each select="adl:entity"> <xsl:variable name="nearside" select="@name"/> <xsl:for-each select="adl:property[@type='entity']"> <xsl:call-template name="referentialintegrity"> <xsl:with-param name="nearside" select="$nearside"/> </xsl:call-template> </xsl:for-each> <xsl:for-each select="adl:property[@type='link']"> <xsl:call-template name="linkintegrity"> <xsl:with-param name="nearside" select="$nearside"/> </xsl:call-template> </xsl:for-each> </xsl:for-each> ------------------------------------------------------------------------------------------------- -- end of file ------------------------------------------------------------------------------------------------- </xsl:template> <xsl:template match="adl:documentation"> /* <xsl:apply-templates/> */ </xsl:template> <xsl:template match="adl:group"> ------------------------------------------------------------------------------------------------- -- security group <xsl:value-of select="@name"/> ------------------------------------------------------------------------------------------------- <xsl:apply-templates select="adl:documentation"/> CREATE GROUP <xsl:value-of select="@name"/>; </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 CONSTRAINT ri_<xsl:value-of select="$nearside"/><xsl:value-of select="concat( '_', @name)"/> FOREIGN KEY ( <xsl:value-of select="@name"/>) REFERENCES <xsl:value-of select="@entity"/> ON DELETE NO ACTION; </xsl:template> <xsl:template name="linkintegrity"> <xsl:param name="nearside"/> <!-- set up referential integrity constraints for link tables --> ALTER TABLE ln_<xsl:value-of select="$nearside"/>_<xsl:value-of select="@entity"/> ADD CONSTRAINT ri_<xsl:value-of select="$nearside"/>_<xsl:value-of select="@entity"/>_<xsl:value-of select="$nearside"/>_id FOREIGN KEY ( <xsl:value-of select="$nearside"/>_id) REFERENCES <xsl:value-of select="$nearside"/> ON DELETE CASCADE; ALTER TABLE ln_<xsl:value-of select="$nearside"/>_<xsl:value-of select="@entity"/> ADD CONSTRAINT ri_<xsl:value-of select="$nearside"/>_<xsl:value-of select="@entity"/>_<xsl:value-of select="@entity"/>_id FOREIGN KEY ( <xsl:value-of select="@entity"/>_id) REFERENCES <xsl:value-of select="@entity"/> ON DELETE CASCADE; </xsl:template> <xsl:template match="adl:entity"> <xsl:variable name="table" select="@name"/> ------------------------------------------------------------------------------------------------- -- primary table <xsl:value-of select="@name"/> ------------------------------------------------------------------------------------------------- CREATE TABLE <xsl:value-of select="@name"/> ( <xsl:for-each select="descendant::adl:property[@type!='link']"> <xsl:apply-templates select="."/><xsl:choose> <xsl:when test="position() = last()"/> <xsl:otherwise>,<xsl:text> </xsl:text></xsl:otherwise> </xsl:choose> </xsl:for-each> ); ---- permissions ------------------------------------------------------------------------------ <xsl:for-each select="adl:permission"> <xsl:call-template name="permission"> <xsl:with-param name="table" select="$table"/> </xsl:call-template> </xsl:for-each> ------------------------------------------------------------------------------------------------- -- convenience view lv<xsl:value-of select="concat( '_', @name)"/> for lists ------------------------------------------------------------------------------------------------- CREATE VIEW lv<xsl:value-of select="concat( '_', @name)"/> AS SELECT <xsl:for-each select="adl: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="adl: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="adl: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>; ---- permissions ------------------------------------------------------------------------------ <xsl:for-each select="adl: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="adl:property[@type='link']"> <xsl:call-template name="linktable"> <xsl:with-param name="nearside" select="$table"/> </xsl:call-template> ---- permissions ------------------------------------------------------------------------------ <xsl:variable name="farside" select="@entity"/> <xsl:for-each select="../permission"> <xsl:call-template name="permission"> <xsl:with-param name="table">ln_<xsl:value-of select="$table"/>_<xsl:value-of select="$farside"/></xsl:with-param> </xsl:call-template> </xsl:for-each> </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. --> Template distinctfield entered, table is <xsl:value-of select="$table"/>. <xsl:for-each select="//entity[@name=$table]/property[@distinct='user' or @distinct='all']"> <xsl:choose> <xsl:when test="@type='entity'"> Entity <xsl:value-of select="@name"/> detected. <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: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 GROUP <xsl:value-of select="@group"/>;</xsl:when> <xsl:when test="@permission='insert'">GRANT INSERT ON <xsl:value-of select="$table"/> TO GROUP <xsl:value-of select="@group"/>;</xsl:when> <xsl:when test="@permission='noedit'">GRANT SELECT, INSERT ON <xsl:value-of select="$table"/> TO GROUP <xsl:value-of select="@group"/>;</xsl:when> <xsl:when test="@permission='edit'">GRANT SELECT, INSERT, UPDATE ON <xsl:value-of select="$table"/> TO GROUP <xsl:value-of select="@group"/>;</xsl:when> <xsl:when test="@permission='all'">GRANT SELECT, INSERT, UPDATE, DELETE ON <xsl:value-of select="$table"/> TO GROUP <xsl:value-of select="@group"/>;</xsl:when> <xsl:otherwise>REVOKE ALL ON <xsl:value-of select="$table"/> FROM GROUP <xsl:value-of select="@group"/>;</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 lv_<xsl:value-of select="$table"/> FROM GROUP <xsl:value-of select="@group"/>;</xsl:when> <xsl:when test="@permission='insert'">REVOKE ALL ON lv_<xsl:value-of select="$table"/> FROM GROUP <xsl:value-of select="@group"/>;</xsl:when> <xsl:otherwise>GRANT SELECT ON lv_<xsl:value-of select="$table"/> TO GROUP <xsl:value-of select="@group"/>;</xsl:otherwise> </xsl:choose> <xsl:text> </xsl:text> </xsl:template> <xsl:template name="linktable"> <xsl:param name="nearside"/> <xsl:variable name="farside"> <xsl:choose> <xsl:when test="@entity = $nearside"><xsl:value-of select="@entity"/>_1</xsl:when> <xsl:otherwise> <xsl:value-of select="@entity"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- create a linking table --> ------------------------------------------------------------------------------------------------- -- link table joining <xsl:value-of select="$nearside"/> with <xsl:value-of select="@entity"/> ------------------------------------------------------------------------------------------------- CREATE TABLE ln_<xsl:value-of select="$nearside"/>_<xsl:value-of select="@entity"/> ( <xsl:value-of select="$nearside"/>_id INT NOT NULL, <xsl:value-of select="$farside"/>_id INT NOT NULL, ); <xsl:text> </xsl:text> <!-- TODO: permissions for link tables! --> </xsl:template> <xsl:template match="adl: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:template> <xsl:template match="adl: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:choose> <xsl:when test="parent::adl:key"> NOT NULL PRIMARY KEY</xsl:when> <xsl:when test="@required='true'"> NOT NULL</xsl:when> </xsl:choose> </xsl:template> <xsl:template match="adl: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:choose> <xsl:when test="parent::adl:key"> NOT NULL PRIMARY KEY</xsl:when> <xsl:when test="@required='true'"> NOT NULL</xsl:when> </xsl:choose> </xsl:template> <xsl:template match="adl:property[@type='integer']"> <xsl:value-of select="@name"/> INT<xsl:if test="string(@default)"> DEFAULT <xsl:value-of select="@default"/></xsl:if><xsl:choose> <xsl:when test="parent::adl:key"> NOT NULL PRIMARY KEY</xsl:when> <xsl:when test="@required='true'"> NOT NULL</xsl:when> </xsl:choose> </xsl:template> <xsl:template match="adl: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:template> <xsl:template match="adl: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:choose> <xsl:when test="parent::adl:key"> NOT NULL PRIMARY KEY</xsl:when> <xsl:when test="@required='true'"> NOT NULL</xsl:when> </xsl:choose> </xsl:template> </xsl:stylesheet>