Friday, January 18, 2019

INFRA Database Design - DBA View


What does the DBA want to see? The DBA wants to see the databases, of course.

Database

So the Database Entity is going to be a central player in the part of the INFRA database that supports DBAs. What is a Database? It is a means of storing, retrieving, and organizing the data that is behind an application system. In short, it is your major tool to save the data and change data into information, A Database is managed by a Database Instance which is a special case of a Service – we’ll talk about Services when we look at some of the other views. In an earlier blog post I talked about databases and database instances in more detail. For our purposes here, a Database Instance is the executing software that manages the files that contain your Database. An instance is an instance of a particular vendor’s or source of database software such as Oracle, SQL Server, Cassandra, or whatever, and one of its attributes will be which version of this software is running.

Application Database

Where the Database entity represents a general term, the more specific meaning of the word, database, is the database objects that are created to store and retrieve the data for a particular application system. To distinguish the two, I have chosen to call this the Application Database. For instance, an HR application might have database objects for information about Employees and Departments, while an Order/Entry application might have objects for Customers, Orders and the Order Line Items in those orders. These objects might be stored in any kind of a database as decided by the developers of that application system. Notice that an Application Database can store data for more than one Application System and an Application System can use data in more than one Application Database. This is in recognition that some data is shared between application systems, though usually only one system may maintain the data while others only read it.

Database Version

My Database Version entity is really an Application Database Version – I just left off the word, “application”, for brevity. The version supports the fact that Application Databases change over time, just as the applications that use them do. So a Database is the target of a deployment of one and only one Application Database Version, but can contain versions of more than one Application Databases. As a general rule, it will contain only one version of any single Application Database. Not shown on the diagram below is the relationship between Application System Versions and Database Versions. Usually a version of an application system is developed side by side with a version of its database, so the Deployment Manager needs to know which two versions correspond. You’ll see this when we get to that view.

Entity Relationship Diagram for DBA’s View


Wednesday, January 16, 2019

INFRA Users

So who is the audience for INFRA?  I’ve identified four groups of people, each with its own perspective on the overall infrastructure:

System Administrators

These are the people who manage your physical and virtual servers.  They want to see what hardware they have, what operating systems (OS) are on them, what virtual machines (VM) and the services on each server.  A service can be a database, an application server, a web server, an LDAP directory, or maybe a microservice – anything needed to operate your applications.

Database Administrators (DBA)

Databases are essential parts of any application system – I have never seen an application that didn’t have a database behind it.  Early in my career, a database may have simply been a set of files.  Later, we were using Relational Database Management Systems (RDBMS) like Oracle, SQL Server, DB2, PostgreSQL or My SQL – and in fact, that is what I’m designing INFRA to use.  Today, you may see other forms of databases, like Hadoop or Cassandra. The DBAs are responsible for managing these important resources.  And of course, this is my particular area of expertise.

Deployment Managers

Eventually, your application needs to be deployed to the various services that operate it.  This is more complex than it used to be.  Early in my career, you compiled the application and just ran the compiled code.  But now you may have different pieces – REST services to provide access to databases, data to enter into LDAP directories, HTML, CSS, Javascript and image files to write to directories accessed by a web server, or JAR, EAR, or WAR files to install on an application server. You may not have a job specifically designated as “Deployment Manager”, but believe me, you have people doing this job.

Application Managers

These are the people responsible for the overall development effort for your applications.  They determine who will do the development work, and what will be developed.  In particular, these people decide which changes will go into each release of an application, and which release will be deployed in which environment.

About the Database Design

Considering that my specialty has been as a database architect, administrator and developer, it is only natural that I would begin the application design with a database design.  I consider the Logical Design to be an important part of the requirements definition of an application.  With this, I find out what “things” - formally called “entities” we are going to collect information about, what information is going to be collected, and how the entities are related to one another.  And once this is engineered to a Physical Design and a development database is created, many tools can use the database design to give you a head start toward developing the rest of the application.  For instance, you can often use a relational table to generate a first cut at the forms that the application will use to enter or update data in the table, or the first cut at pages that show this data.

And since I have been a DBA, it is only natural for me to start by designing the part of the database that will contain the information that I would most want to see in my view of INFRA.  That is the subject of my next post.

Tuesday, January 15, 2019

Introducing INFRA – an Application to View Your Infrastructure

On my last two projects, I was the database architect and administrator.  Although our projects were small, we needed to maintain multiple sets of servers, each with different versions of the applications, databases, identity services, and other support services.  We had a development environment, a test environment, a user acceptance test environment, a pre-production (aka staging) environment and of course, production.  While some of these shared servers, others needed separate servers.  Most servers were virtual machines, but not all. So it became difficult to answer questions like:

  • Which server is running our development Oracle database?
  • Is the test application running against the test database?
  • How can we quickly reset the user acceptance test environment for a new round of UAT?
  • The backup files for the production SQL Server database need to be written to separate physical hardware. Which servers with room for these files meet this requirement?
  • Can we take this server down for an upgrade today?
  • What version of the application is on the staging application server?

I’ve been thinking about this problem on and off for several years.  I’ve done some research, and I can’t seem to find a COTS application that stores this information.  There are source control systems that can distinguish between the source code for different versions of an application, and I use this for different versions of database objects.  JIRA, which is an application that we use a lot for task and bug tracking, can list Releases of an application, lets you identify changes for each release, and can read the data in your source control system to tie releases to the source code that was changed.  Virtual Machine (VM) control software used by our system administrators, like the control software for VMWare can identify which virtual servers are running and which physical servers are hosting them.  But nothing ties all of this information together.

So, being between jobs, I decided to start writing a new application to keep the lists of servers, databases, application releases, and other services.  And especially, to keep the relationships between them – what release of the application is on this server, which databases it is using, where are the user ids and passwords that can access this application on this server. The goal is to be able to answer the questions listed above and others like them.

I call my new application, INFRA.  It is a work in progress, but I thought I’d share the process with my readers.  The initial database design is pretty much done, so I’ll start with that.  I’ll be inviting you to share your thoughts too.  Tell me where I’ve gotten it right, where I’ve gotten it wrong.  Tell me if the whole idea is stupid – I’d love to hear that someone has already solved this problem.

Wednesday, October 3, 2018

Source Control for and deployment of your Database

I just read Jeff Smith's latest blog entry about exporting the DDL for your Oracle database with Oracle SQL Developer.  You can do a similar export with TOAD - both the original version for Oracle and the version for SQL Server.  Other tools have this ability too.  Jeff hints at why you might want to use this feature - to put your database under source control.

I have had the experience of walking into a shop where my predecessor database designer/developer had not done this, and I was shocked.  The application developers - the Java programmers, the JavaScript framework developers, the UI developers were all very careful to keep their source code in a source code repository, either with Subversion or Git, so I did not understand why the database guy/gal hadn't done the same with the code to create the database objects used by the application. DDL is SOURCE CODE, and it belongs in the source control system. So if you haven't done it already, follow Jeff's instructions (here) or the instructions for doing the same thing with your favorite database IDE and then put the resulting DDL code into your source code repository.  If you don't know how to use your shop's source code control tools, ask your fellow developers for help (by the way, this is a nice way to ingratiate yourself with the developers), or ask your favorite search engine.  What? Your shop doesn't do source control?  Convince the powers that be that this is ESSENTIAL to a good application development project and there are good tools available at no cost.  Do it NOW - I'll wait.

There is one more thing that goes in source control: scripts to load data into the look-up and reference tables.  These might be SQL scripts with INSERT commands. I do that, and the scripts go in a folder named "Data".  Or in Oracle, you might have data files (maybe CSV) with the SQL*Loader control files to load the data.  Your database IDE like SQL Developer can help you with this if the data is already in your development database.  If reference or look-up tables are ever changed by users, you will need to update this data and check the changes into source control.  You need a plan for propagating the latest changes to all environments.  The key is that you should consider the data in these tables as if it was DDL.

So now, you have captured all the DDL for the database objects your application uses and put it in source code control.  What next?  Most of the tools that let you export your DDL, also create a script to run the DDL.  This would let you re-create your objects in a new database, such as when it is time to deploy to the test database.  We always have at least four environments - development, test, staging (sometimes called pre-production), and production. I have been in shops where the production database is under the control of a production database administrator (DBA), and the development DBA (often me) does not have rights to run DDL in production.  The process is that when a production release is authorized, the production DBA does the database deployment.  So my deployment script is always thoroughly tested, and written so that even someone who knows NOTHING about the application can run it.  If your tool doesn't write a deployment script, you'll need to write it by hand.  I often use (in Windows) the DIR command to list the DDL files, spooling the output to a file, then edit the file. And yes, deployment scripts go to source control - mine go in a folder named "Scripts".

So what about database deployment?  The initial deployment is easy. The deployment script just runs ALL the DDL and data load scripts.  But subsequent deployments are a bit harder.  Views and stored procedures can use the CREATE OR REPLACE syntax (or CREATE OR ALTER in SQL Server) Once a table has been created and contains data, you can't just DROP it and CREATE a new version. You need to run ALTER commands to make some changes, and for bigger changes you might need to do something like:
CREATE TABLE my_table_new ...
INSERT INTO my_table_new (...)
  SELECT ...
    FROM my_table
DROP my_table;
RENAME my_table_new TO my_table;
I will usually name my deployment script for each new database release with the name of the release, like "myapp_2.1_DEPLOY.sql".  Since we use JIRA to manage tasks, I will have a script named after the JIRA task that does the changes associated with that task, such as "MYAPP-123.sql".  If the changes involve DDL for an object that can use "CREATE OR REPLACE" syntax, MYAPP-123.sql will simply call the new version of the DDL script for that object.  If not, MYAPP-123.sql will contain the ALTER (and other) commands needed.  When I create the release deployment script, it calls each of the scripts for the JIRA tasks included in that release.  All of these go in the Scripts folder.  I also tend to update the CREATE scripts for tables, even if deployment will not be recreating the table - that way I can create a new empty database without running all the ALTER commands.

By the way, if you are using Oracle 11gR2 or later, you probably want to use Edition Based Redefinition in your deployment scripts. This is not used as often as it should be, and will let you do your database deployments in production with no downtime.

Hope this inspires you to put you DDL under source control, and give you some ideas how to organize your database deployments.

Friday, September 21, 2018

DBMS_OUTPUT Reports in Oracle SQL Developer

Although I've been working a lot with SQL Server databases lately, I've never entirely left the Oracle world.  For instance, I've used SQL Developer Data Modeler (SDDM) to design my database, and exported my models to a reporting repository in an Oracle XE database.  What kind of reporting?  Well, for one thing, Oracle SQL Developer has a very good report generator built into it, and comes with a number of pre-written reports, including some for SDDM.  Here is the report window:

You can also build your own reports.  Have a look at Jeff Smith's blog (http://www.thatjeffsmith.com/) for lots of great information about SQL Developer and SDDM.  Specifically, look at the bottom of this page for links to Jeff's posts about the reporting capability: https://www.thatjeffsmith.com/sql-developer/.  One good way to build your own report is to copy one of the built-in reports and modify it.

But, what if I want something that none of the built-in reports has, or I want to do formatting that the reporting tools don't do natively?  For instance, what if you want to print a report something like this?
  
This is a data dictionary for the HR sample schema formatted according to my client's wishes for data dictionaries.  There are very good reports from the Oracle data dictionary that are built into SQL Developer, and I like the master child formatting they have where you can click on the table name and see information about the table's columns, but this is more of a report that you can print off if you need to, or read into an Excel spreadsheet.

No worries.  You can write your own report in PL/SQL to produce HTML that you output with the DBMS_OUTPUT built-in package.  Then in SQL Developer you create it as a PL/SQL DBMS_OUTPUT style report:

The query for this one is:
BEGIN
  DATA_DICTIONARY;
END;
So how did I write this?  You COULD just hand write the PL/SQL, but since this is going to produce HTML, I like to write it as a PL/SQL server page.  I've written extensively about the many uses for PL/SQL server pages (PSPs) before, for instance, see this about using a PSP to produce Spreadsheet ML: https://it.toolbox.com/blogs/jjflash/spreadsheetml-in-a-pl-sql-server-page-part-1-of-2-022015.

Basically, a PSP is an HTML page with some PL/SQL embedded in it.  The PL/SQL is used for flow control like LOOPs and to embed data from the database into the page.  You compile it with the LOADPSP utility that comes with all Oracle databases, including Oracle XE.  And the compiled PSP is actually a stored procedure that uses the PL/SQL Web Toolkit to write to the web - this is the same underlying technology as is used by Oracle Application Express - i.e. APEX.  The specific toolkit procedure the compiled PSP uses is HTP.PRN which outputs all of the HTML.   But we aren't going to write to the web, we want to write our HTML to DBMS_OUTPUT.

So what I did was compile my PSP with LOADPSP, then edited the resulting stored procedure, named DATA_DICTIONARY.  I made three changes:

  1. Since the underlying SQL uses the USER_* data dictionary views, I wanted to make sure it shows the tables owned by the user that executes it.  So I added "AUTHID CURRENT_USER" to the procedure definition.  That makes it an Invoker's Rights procedure.
  2. I did a FIND/REPLACE to change all occurrences of HTP.PRN to DBMS_OUTPUT.PUT.
  3. I found out that when you are writing to the DBMS_OUTPUT buffer, nothing gets written until there is a call to DBMS_OUTPUT.PUT_LINE;  So I added "DBMS_OUTPUT.PUT_LINE('');" as the last line of the procedure.
Recompiled, and it all worked. Almost.  It seems that SQL Developer ignores your <head> section, including any <style> section you might have put there.  There are several possible work-arounds, but what I advise is to put any styling that you want into "style" attributes in the HTML elements to be styled.  Yes, I know this is against HTML best practices, but what can you do?

So I hope this inspires you to try the SQL Developer reporting features.  And if the need arises, write your own reports your way.

Friday, September 14, 2018

SDDM Script to Create SQL Server Journal Tables

In my previous post, I talked about how Oracle SQL Developer Data Modeler (SDDM) is extendable with scripts written in languages that support JSR-223.  One of those languages is Groovy (http://www.groovy-lang.org/) and I showed how to add Groovy to the JVM classpath used by SDDM.  You might want to visit that post to see how.  The reason that I needed to write a script was that I wanted to easily add Journal Tables and the triggers for updating them to my database DDL.  SDDM actually comes with a script for doing this, but the script writes the triggers in PL/SQL for Oracle databases.  As my readers know, the database I was designing was a Microsoft SQL Server database, and the triggers needed to be written in Transact-SQL (T-SQL).

About Journal Tables

So, first of all, you might ask, is what is a Journal Table?  A Journal Table is a table that captures an before or after image of every change to a row in the table that is being monitored, usually with a date/time to show when the change occurred.  The idea is that we be able to reconstruct a history of changes to the data in a table.  Because of the overhead, you probably don't want a journal table behind all of your tables, but when you need to track who did it, when was it done, and what exactly was changed, a journal table can be a good solution.  It would also help to recover from changes that shouldn't have been done.  There are other solutions like Oracle's flashback query capabilities, but some databases can't do that, and some tables require a little more precise control. By the way, it is often necessary to set permissions on journal tables more stringently than permissions on the tables being journaled, so that hackers can't cover their tracks.

I personally prefer to make these before images, so that you can see what the row looked like before the change.  This would imply that you need before UPDATE and before DELETE triggers that INSERT the row of the table being journaled as it exists before the change to the journal table, but no before INSERT since before the INSERT the row didn't exist.  But my development lead wanted an after image, which is fine, since SQL Server only has AFTER statement triggers.  Here is an example of a table to be journaled, and a journal table:
CREATE TABLE my_table (
  my_id        INTEGER,
  my_char_data VARCHAR(30)
);
CREATE TABLE my_table_jn (
  my_id        INTEGER,
  my_char_data VARCHAR(30),
  operation    VARCHAR(10),
  date_changed DATETIME
);
If I do (on September 10):
INSERT INTO my_table (my_id, my_char_data)
   VALUES (1,'Example 1');
The after INSERT trigger should do:
INSERT INTO my_table_jn (my_id, my_char_data, operation, date_changed)
VALUES (1, 'Example1','INSERT', CONVERT(datetime,'09/10/2018',101));
If I do (on September 12):
UPDATE my_table SET my_char_data = 'Example2'
  WHERE my_id = 1;
The after UPDATE trigger should do:
INSERT INTO my_table_jn (my_id, my_char_data, operation, date_changed)
VALUES (1, 'Example2','UPDATE', CONVERT(datetime,'09/12/2018',101));
If I do (on September 14):
DELETE my_table
  WHERE my_id = 1;
The after DELETE trigger should do:
INSERT INTO my_table_jn (my_id, my_char_data, operation, date_changed)
VALUES (1, 'Example2','DELETE', CONVERT(datetime,'09/14/2018',101));
The data for DELETE is actually a before image, since after the delete there is no data.  By the way, triggers participate in the underlying transaction, so if the change to my_table is rolled back, so will the INSERT into my_table_jn.

The Script

As I said before, SDDM includes a script for adding code for Journal Tables to your DDL.  Though I couldn't use Oracle's script as written, it served as an excellent starting point for my version.  It also shows how you get access to the underlying SDDM data.  Here is the first part of the script:
/*
Writes CREATE commands for Journal Table and Triggers for SQL Server.
variable ddlStatementsList should be used to return the list with DDL statements
that are created by script - as shown below:
ddlStatementsList.add(new java.lang.String(ddl));
other available variables:
- model - relational model instance
- pModel - physical model instance
- table - the table in relational model
- tableProxy - table definition in physical model 
 */
Since the original script is written in Javascript, and mine is written in Groovy, I needed to change the syntax to Groovy, but much is the same or similar, including comments. Notice that SDDM hands you access points to the SDDM data - listed in the comments above.  But it doesn't tell you how to write to SDDM's log.  Fortunately, Dave Schleis provided the following code:
// get a handle to the application object
def app = oracle.dbtools.crest.swingui.ApplicationView
app.log("Creating DDL for Journal table for ${table.name}");
"ddl" is a variable to hold the code to be added to the ddl being exported for the table to be journaled.  In the original, this was a string variable, but strings in Groovy are immutable.  When you do "ddl = ddl + 'a string'" you are really creating a new string object.  So I changed it to a StringBuilder, which in Groovy and Java is an object to which you can append more data, without the waste of discarding old strings and creating new ones.
StringBuilder ddl;
String lname;
//journal table name suffix 
jnTabSuf = "_jn";
// trigger name suffix
jnAISuf = "_jn_ai";
jnAUSuf = "_jn_au";
jnADSuf = "_jn_ad";
prompt = model.appView.settings.includePromptInDDL;
useSchema = model.appView.settings.isIncludeSchemaInDDL();
if(model.storageDesign.open){
    if(useSchema){
        lname = tableProxy.longName;
    }else{
        lname = tableProxy.name;
    }
}else{
    if(useSchema){
        lname = table.longName;
    }else{
        lname = table.name;
    }
}
Here you will see a major advantage of using Groovy for your DDL Transformation (and other) scripts.  Groovy has a GString type, similar to strings in Java and Javascript, but you can embed variables in your GStrings.  In other languages you would have to concatenate strings.  This is a great space and time saver when the script is really code that writes code.
if(prompt){
    ddl = new StringBuilder("PRINT 'Creating Journal Table for ${lname};'\n");
}else{
    ddl = new StringBuilder("");
}
app.log("Creating Journal Table DDL.");
Most of the rest of the code is appending strings (GStrings) to the ddl variable.  Groovy overloads the "append()" method of StringBuilder to the "<<" operator, once again saving me a little time and space. Also notice that I'm using the triple quoted string in this section of code, which lets me use actual line feeds in place of the "\n" line feed character.  I didn't do this throughout, because I didn't want to fool with the original Javascript code more than necessary.
ddl <<
"""CREATE TABLE ${lname}${jnTabSuf}
  (${table.name}${jnTabSuf}_id INT IDENTITY(1,1) NOT NULL
  ,operation VARCHAR(10) NOT NULL
""";
cols = table.elements;
Here, I'm looping through the columns from the original table and writing the journal table with the same columns.  I don't add the date_changed column, because my tables already have a last_update_date column.  They also have a last_update_user_id.  You could probably add code that says to add these to the journal table if they don't exist.
cols.each {
    ddl <<
    "  ,$it.name $it.datatypeString";
    if (it.mandatory){
        ddl << " NOT NULL\n";
    }else{
        ddl << "\n";
    }
}
/* Primary key is non-clustered because queries of the Journal table will
 * usually be by the parent table's key.
 */
ddl <<
    "  ,CONSTRAINT ${table.name}${jnTabSuf}_pk\n" +
    "     PRIMARY KEY NONCLUSTERED (${table.name}${jnTabSuf}_id)\n" +
    " );\n" +
    "GO\n\n" + 
/* So instead, we create a clustered index on the parent table's PK.
 * No Foreign key, because we may delete rows of the parent table and still
 * keep the journal of changes to the now-deleted rows, including the datetime of
 * deletion.
 */
"CREATE CLUSTERED INDEX ${table.name}${jnTabSuf}_fki\n" +
    "  ON ${table.name}${jnTabSuf} (${table.name}_id);\n" +
    "GO\n"

if(prompt){
    ddl << "\nPRINT 'Creating Journal Triggers for ${lname};'\n";
}else{
    ddl << "\n";
}
So far, my code has been similar to the original, but my triggers are very different - SQL Server triggers are not at all like Oracle triggers. Leave a comment, if you want me to write a post to compare the way it works in SQL Server with triggers to do it in Oracle.
app.log("Creating Journal Table After Insert trigger.");
ddl <<
  "DROP TRIGGER IF EXISTS ${lname}${jnAISuf};\n" +
  "GO\n" +
  "CREATE TRIGGER ${table.name}${jnAISuf}\n" +
  "  ON ${lname}\n" +
  "  AFTER INSERT AS\n" +
  "BEGIN \n" +
  "  INSERT INTO ${lname}${jnTabSuf}\n" +
  "    (operation\n";
cols.each {
    ddl <<
    "    ,$it.name\n";  
}
ddl <<
    "    )\n" +
  "  SELECT 'INSERT' AS operation\n"
cols.each {
    ddl <<
    "    ,$it.name\n";  
}
ddl <<
    "    FROM inserted;\n" +
    "END;\n" +
    "GO\n\n";
The rest of the code is pretty similar.
app.log("Creating Journal Table After Update trigger.");
ddl <<
  "DROP TRIGGER IF EXISTS ${lname}${jnAUSuf};\n" +
  "GO\n" +
  "CREATE TRIGGER ${table.name}${jnAUSuf}\n" +
  "  ON ${lname}\n" +
  "  AFTER UPDATE AS\n" +
  "BEGIN \n" +
  "  UPDATE t\n" +
  "      SET t.last_update_date = CURRENT_TIMESTAMP\n" +
  "    FROM ${lname} AS t\n" +
  "    INNER JOIN inserted AS i\n" +
  "       ON t.${table.name}_id = i.${table.name}_id;\n\n" +
  "  INSERT INTO ${lname}${jnTabSuf}\n" +
  "    (operation\n";
cols.each {
    ddl <<
    "    ,$it.name\n";  
}
ddl <<
  "    )\n" +
  "  SELECT 'UPDATE' AS operation\n"
cols.each {
    ddl <<
    "    ,$it.name\n";  
}
ddl <<
    "    FROM inserted;\n" +
    "END;\n"+
    "GO\n\n"
app.log("Creating Journal Table After Delete trigger.");
ddl <<
  "DROP TRIGGER IF EXISTS ${lname}${jnADSuf};\n" +
  "GO\n" +
  "CREATE TRIGGER ${table.name}${jnADSuf}\n" +
  "  ON ${lname}\n" +
  "  AFTER DELETE AS\n" +
  "BEGIN \n" +
  "  INSERT INTO ${lname}${jnTabSuf}\n" +
  "    (operation\n";
cols.each {
    ddl <<
    "    ,$it.name\n";  
}
ddl <<
  "    )\n" +
  "  SELECT 'DELETE' AS operation\n"
cols.each {
    if (it.name == "last_update_date") {
ddl <<
    "    ,CURRENT_TIMESTAMP AS $it.name\n";  
    } else {
        ddl <<
    "    ,$it.name\n";  
    }
}
ddl <<
    "    FROM deleted;\n" +
    "END;\n"+
    "GO\n"
The last step is to add the ddl variable that I've been building to the DDL that will be exported.  Since my variable is a StringBuilder, not a String,we just need to use its toString method.
ddlStatementsList.add(ddl.toString());

So that's it.  You are welcome to cut and paste this into your own SDDM project.  Hope this was useful.

Friday, August 24, 2018

Scripting Oracle SQL Developer Data Modeler with Groovy

In my last post, I related how I am using Oracle SQL Developer Data Modeler (SDDM) to design a database to be implemented in a Microsoft SQL Server database.  I mentioned that one of the neat things about SDDM is that you can write scripts to do things that the tool doesn't do natively.  Scripts can be written in any language supported by the Java Scripting API (defined by JSR 223). Nashorn, the Java library for scripting in JavaScript is built into Java, so scripting in JavaScript works out of the box.  The SDDM developers have included a number of Nashorn scripts with the tool.  You can use these yourself, or use them as examples from which you can write your own scripts.

SDDM also comes with a good number of scripts written in JRuby - the JVM implementation of the Ruby language.  But to use the JRuby scripts, or scripts written in some other compliant scripting language, you need to copy the Java library(s) for that language to your SDDM classpath.  The easiest way to find a good place to put them is to go to the Help/About page, select the Properties tab, and find the property named java.ext.dirs:

With SDDM shut down, put the library in one of the directories listed in this property.  Then you can re-start SDDM. Mine is in %SDDM_HOME%\jdk\jre\lib\ext where SDDM_HOME is the root directory where you installed SDDM.  This information, and much of what I have learned about scripting SDDM, came from Dave Schleis, especially his blog post, Data Modeler Scripting-101-Lets start at the very beginning.

DDL Transformation Script for Journal Table

What I needed was a script to generate the DDL to create a journal table behind one of my tables, plus the database triggers to automatically write to the journal table every time DML is executed against the base table.  SDDM happens to come with a script, written in JavaScript (for Nashorn) to do this.  If you are designing an Oracle database, you may be able to use this script out of the box.  But I am writing for a SQL Server database, and while the CREATE TABLE for the journal table is almost identical, triggers in SQL Server are VERY different.  Not only are they written in T-SQL, which is quite different from PL/SQL, but triggers all run as statement level triggers, not row level triggers.  So I was glad to have the original script as an example, but I needed to do major changes.

Writing scripts in Groovy

Now, I could have kept the script in JavaScript, but I don't know that language very well.  I don't know Ruby very well either.  Dave Schleis's favorite language is Apache Groovy which like JRuby does support the JSR-223 standard, so some of his examples are in Groovy.  And I've gotten familiar with Groovy because of my past experience with Oracle Application Development Framework (ADF).  ADF's Model component, ADF Business Objects, can be extended with scripts written in Groovy.  So I decided to write my DDL Transformation for SQL Server Journal Tables in Groovy.

To write SDDM scripts in Groovy, all you need to do is copy the Groovy library to the SDDM classpath as I described above.  Download a copy of Groovy from http://www.groovy-lang.org/download.html.  While the latest stable version of Groovy is 2.5, it does not contain the library needed to support JSR-223.  So download the binary for the latest 2.4 version - I downloaded apache-groovy-binary-2.4.15.zip.  The library you want is groovy-all-2.4.15.jar, found in the "embeddable" directory.

To write scripts, select Tools/Design Rules and Transformations... from the SDDM menu.  The kind of script I wanted to write is a Table DDL Transformation, which can be run when exporting the DDL for your Relational/Physical design.  Choose the Relational Model for your script.  Then you will see:

I have already added my script. Notice that there are four possible scripts for each script set - one to add DDL code before the CREATE TABLE commands, one to actually replace the CREATE TABLE commands, one to add code after the CREATE TABLE, and one to add code at the end of the DDL for all the tables being exported.  You don't need to use all four, and in fact, my journal table code is only for After Create.  You won't be able to select a scripting engine until after you start writing the script - I'm not sure why. So start by just adding a comment or a few spaces. I started by selecting the Journal tables script that comes with SDDM and copying it, then pasting it into my new script. Then pick a scripting engine.  If you have correctly added the Groovy library to the classpath, "Groovy Scripting Engine" should be one of the choices.  Warning - there is currently (18.1 for me) a bug in SDDM where SDDM doesn't save the scripting engine choice for Groovy Scripting Engine.  Oddly, it does for Oracle Nashorn, the default, and for JRuby.  The work-around is to go into dr_custom_scripts.xml find the reference to your script and change the engine attribute. I'd make a back-up of this file first.

So in the next post, I'll go through the code for my script and talk a little about testing it.