Skip to content
Snippets Groups Projects
Commit e871f452 authored by Johannes Goderbauer's avatar Johannes Goderbauer
Browse files

Liquibase_lib: first version

parent 813dc641
No related branches found
No related tags found
No related merge requests found
......@@ -3,4 +3,7 @@
<name>Liquibase_lib</name>
<majorModelMode>DISTRIBUTED</majorModelMode>
<process>%aditoprj%/process/Liquibase_lib/process.js</process>
<variants>
<element>LIBRARY</element>
</variants>
</process>
import("system.translate");
import("Util_lib");
import("system.fileIO");
import("system.datetime");
import("system.SQLTYPES");
import("system.db");
import("system.util");
/**
* functions for various Liquibase-actions
* Do not create an instance of this!
*
* @class
* @static
*/
function LiquiUtils(){}
LiquiUtils.exportToLiquibase = function(pAuthor, pTableName, pColumns, pNewUUIDForIndexes, pCondition)
/**
* exports all tables(except the liquibase-tables "DATABASECHANGELOG" and "DATABASECHANGELOGLOCK") from the database as files into a specific
* server-folder; these files are in the liquibase-changeset-format to apply them via liquibase to a database
* when a long clob-value or a blob value is exported subfolders are created with files for it's contents (no ending for blobs, .txt-ending for clobs)
*
* @param {String} pOutFolderPath path where the files and folders will be created; this folder should be empty; it must be on the server
* @param {String} [pAuthor="autogenerated"] author that will be written into the liquibase-changesets
* @param {String} [pAlias=current db-alias] alias where the data will be loaded from
* @param {Array} [pExcludedTables=none] tables that will be excluded from the tables that are exported; case-insensitive;
* note that the liquibase tables "DATABASECHANGELOG" and "DATABASECHANGELOGLOCK" will never be exported
* (regardless of what you specify here)
* @param {Boolean} [pIncludeClearTableDirective=false] if true, a delete element is added at the beginning of the changeset for the table
*
* @return void
*
*/
LiquiUtils.exportAllTablesAsLiquibaseFiles = function(pOutFolderPath, pAuthor, pAlias, pExcludedTables, pIncludeClearTableDirective)
{
if(!pNewUUIDForIndexes)
pNewUUIDForIndexes = []
var xmlData = <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"/>;
xmlData.changeSet.@author = pAuthor
xmlData.changeSet.@id = util.getNewUUID()
var excludedTables = ["DATABASECHANGELOG", "DATABASECHANGELOGLOCK"];//liquibase-tables - therefor they shall never be included
if (pExcludedTables)
excludedTables = excludedTables.concat(pExcludedTables);
var alias = pAlias || db.getCurrentAlias();
var tables = db.getTables(alias);
for (var i = 0, l = tables.length; i < l; i++)
{
if (ArrayUtils.hasElement(excludedTables, tables[i], true))
continue;
var columns = db.getColumns(tables[i], alias);
var cond = null;
LiquiUtils.exportTableAsLiquibaseFiles(pOutFolderPath, tables[i], columns, cond, pAuthor, pIncludeClearTableDirective, alias);
}
};
/**
* exports a single table from the database as liquibase-changeset-file(s) into a specific server-folder
* when a long clob-value or a blob value is exported subfolders are created with files for it's contents (no ending for blobs, .txt-ending for clobs)
*
* @param {String} pPath file-path where the files and folders will be created; the folder must be on the server
* @param {String} pTableName name of the DB-table that will be exported; this is also the name of the file that is stored
* @param {Array} pColumns db-columns within the table that will be exported
* @param {String} [pCondition=none] db-condition to limit the data that will be exported; if nothing given the whole content will be exported
* @param {String} [pAuthor="autogenerated"] author that will be written into the liquibase-changeset
* @param {Boolean} [pIncludeClearTableDirective=false] if true, a delete element is added at the beginning of the changeset for the table
* @param {String} [pAlias=current db-alias] alias where the data will be loaded from
*
* @return {null} returns currently always null
*/
LiquiUtils.exportTableAsLiquibaseFiles = function(pPath, pTableName, pColumns, pCondition, pAuthor, pIncludeClearTableDirective, pAlias)
{
var resXml = LiquiUtils._getDataXml(pAuthor, pPath, pTableName, pColumns, pCondition, pIncludeClearTableDirective, pAlias);
if (resXml == "")
return null;
var dbData = db.table("select " + pColumns.join(", ") + " from " + pTableName + " " + (pCondition ? pCondition : ""));
var types = db.getColumnTypes(pTableName, pColumns);
fileIO.mkdir(pPath, true);
var fullFileName = pPath += "/" + pTableName + ".xml";
fileIO.storeData(fullFileName, resXml, util.DATA_TEXT, false, "UTF8");
return null;
};
/**
* generates from a table a xml-changeset for liquibase; determines the correct liquibase-xml-attribute by db-column-type;
* if a value is empty it will not be added to the xml; will load the data at once (no paging);
* if the data contains a long CLOB-value that value has to and will be stored on the server-filesystem;
* if the data contains any BLOB-value that value has to and will be stored on the server-filesystem;
*
* @param {String} [pAuthor="autogenerated"] author that will be written into the changeset
* @param {String} [pLobPath=not set] file-path where lob files and folders will be created; the folder must be on the server;
* if you've no lob-fields you don't have to specify somehing here
* @param {String} pTableName name of the DB-table that will be exported; this is also the name of the file that is stored
* @param {Array} pColumns db-columns within the table that will be exported
* @param {String} [pCondition=none] db-condition to limit the data that will be exported; if nothing given the whole content will be exported
* @param {Boolean} [pIncludeClearTableDirective=false] if true, a delete element is added at the beginning of the changeset for the table
* @param {String} [pAlias=current db-alias] alias where the data will be loaded from
*
* @return {String} the liquibase-changest in xml-form
*/
LiquiUtils._getDataXml = function(pAuthor, pLobPath, pTableName, pColumns, pCondition, pIncludeClearTableDirective, pAlias)
{
var author = pAuthor || "autogenerated";
var alias = pAlias || db.getCurrentAlias();
//cannot be added within jdito code to the XML-object, so instead add it as string
var XML_HEADER_LINE= '<?xml version="1.1" encoding="UTF-8" standalone="no"?>';
dbData.forEach(function(pRow) {
var node = <insert/>;
node.@tableName = pTableName;
for (let i = 0; i < pColumns.length; i++)
var dbData = db.table("select " + pColumns.join(", ") + " from " + pTableName + " " + (pCondition ? pCondition : ""), alias);
if (dbData.length == 0)
return "";
var colTypes = db.getColumnTypes(pTableName, pColumns, alias);
//every type has its function that accepts a columnName- and a value-parameter
//so let's dertermine once the corresponding funtion to its type for faster access
//(the function will only depend on the type and not change per datarow - so no need to determine the correct function per datarow)
var colCallbackFn = colTypes.map(function (colType){
if (SQLTYPES.isNumberType(colType))
return LiquiXTable.prototype.addNumberCol;
else if(SQLTYPES.isDateType(colType))
{
var value = "";
for (j = 0; j < pNewUUIDForIndexes.length; j++) {
if (i == pNewUUIDForIndexes[j])
{
value = util.getNewUUID();
break;
}
}
if (!value)
value = pRow[i];
var col = <column/>;
col.@name = pColumns[i];
if (colType == SQLTYPES.DATE)
return LiquiXTable.prototype.addDateCol;
else
return LiquiXTable.prototype.addDateTimeCol;
}
else if(SQLTYPES.isLOBType(colType))
{
if (colType == SQLTYPES.BLOB)
return LiquiXTable.prototype.addBlobCol;
else
return LiquiXTable.prototype.addClobCol;
}
else
return LiquiXTable.prototype.addStrCol;
});
var columnLen = pColumns.length;
var changeLogXml = LiquiXmlUtils.databaseChangeLog(author);
if (pIncludeClearTableDirective)
changeLogXml.changeSet.appendChild(<delete tableName={pTableName}/>);
dbData.forEach(function(row)
{
var tableXml = LiquiXTable.make(pTableName);
if (pLobPath)
tableXml.setLobBasePath(pLobPath);
for (var i = 0; i < columnLen; i++)
{
var colName = pColumns[i];
var value = row[i];
var fnToUse = colCallbackFn[i];
if (fnToUse != null)
fnToUse.call(tableXml, colName, value)
}
changeLogXml.changeSet.insert += tableXml.xml;
});
return XML_HEADER_LINE + "\n" + changeLogXml;
};
/**
* contains functions which create small fragments of XML-objects for liquibase-changesets
* Do not create an instance of this!
*
* @class
* @static
*/
function LiquiXmlUtils(){}
//TODO: comments
LiquiXmlUtils.databaseChangeLog = function (pAuthor, pChangeSetId)
{
if (pChangeSetId == undefined)
pChangeSetId = util.getNewUUID();
var dbcl = <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd"/>
;
// do not add value if null
if (value)
{
if (SQLTYPES.isNumberType(types[i]))
col.@valueNumeric = value;
else
col.@value = value;
}
dbcl.changeSet = <changeSet author={pAuthor} id={pChangeSetId} />;
return dbcl;
};
LiquiXmlUtils.colValue = function (name, value, valueAttribute)
{
var DO_NOT_ADD_EMPTY_VALUES = true;//clob and blob will ignore this setting and always skip empty values
if (DO_NOT_ADD_EMPTY_VALUES && value == "")
return null;
var x = <column {valueAttribute}={value}/>;
x.@name = name;
return x;
};
LiquiXmlUtils.colStr = function (name, valueStr)
{
return LiquiXmlUtils.colValue(name, valueStr, "value");
};
LiquiXmlUtils.colDateTime = function (name, valueDate, type)
{
if (valueDate.toString() === parseInt(valueDate, 10).toString())
{
if (type == "date")
valueDate = datetime.toDate(valueDate, "yyyy-MM-dd");
else
valueDate = datetime.toDate(valueDate, "yyyy-MM-dd'T'hh:mm:ss");
}
//Unterscheiden zwischen datetime & date!!
return LiquiXmlUtils.colValue(name, valueDate, "valueDate");
};
LiquiXmlUtils.colNumber = function (name, valueNumber)
{
return LiquiXmlUtils.colValue(name, valueNumber, "valueNumeric");
};
LiquiXmlUtils.tableIns = function (table)
{
var x = <insert tableName={table} />;
return x;
};
LiquiXmlUtils._resolveId = function (pIdValue)
{
return pIdValue;
};
/**
* represents a liquibase-insert-into-table changeset element (as XML) and provides functions which make it easier to add columns-entries with values
*
* @class
*/
function LiquiXTable(pName)
{
this._name= pName;
this._mode = "insert";
this.setLobBasePath("");
this.xml = LiquiXmlUtils.tableIns(pName);
}
LiquiXTable.make = function(pName)
{
return new LiquiXTable(pName);
};
LiquiXTable.prototype.setLobBasePath = function(pPath)
{
//to build later the lob-path corretly it's necessary to always have the last symbol a folder-splitter-char
//but it's okay to have no lobPath at all IF no long clob value and no blob value will be added
if (pPath)
{
var lastChar = pPath.slice(-1);
if (lastChar != "\\" && lastChar != "/")
pPath = pPath + "/";
}
node.appendChild(col);
}
this.appendChild(node);
}, xmlData.changeSet);
return xmlData.toXMLString()
this._lobBasePath = pPath;
};
LiquiXTable.prototype.appendIfNotNull = function(xmlToAppend)
{
if (xmlToAppend != null)
this.xml[this._mode] += xmlToAppend;
return this;
};
LiquiXTable.prototype.addStrCol = function (pColName, pValue)
{
return this.appendIfNotNull(LiquiXmlUtils.colStr(pColName, pValue));
};
LiquiXTable.prototype.addClobCol = function (pColName, pValue)
{
if (pValue == "")
return this;
else if (pValue.length <= 1000)
return this.addStrCol(pColName, pValue);
else
{
var lobBasePath = this._lobBasePath;
if (!lobBasePath)
throw new Error(translate.withArguments("[%0]it was necessary to create a text-file from a clob-field but no lob-file-path was specified."
+ "The lob-file-path is required when adding long clob-values. value length was %1.", ["LiquiXTable addClobCol", pValue.length]));
var relativeFolderPath = this._name + "/" + pColName + "/clobFiles/";
var fileName = util.getNewUUID() + ".txt";
fileIO.mkdir(lobBasePath + relativeFolderPath, true);
fileIO.storeData(lobBasePath + relativeFolderPath + fileName, pValue, util.DATA_TEXT, false, "UTF8");
return this.appendIfNotNull(LiquiXmlUtils.colValue(pColName, relativeFolderPath + fileName, "valueClobFile"));
}
};
LiquiXTable.prototype.addBlobCol = function (pColName, pValue)
{
if (pValue == "")
return this;
var lobBasePath = this._lobBasePath;
if (!lobBasePath)
throw new Error(translate.withArguments("[%0]it was necessary to create a file from a blob-field but no lob-file-path was specified."
+ "The lob-file-path is required when adding blob-values.", ["LiquiXTable addClobCol"]));
var relativeFolderPath = this._name + "/" + pColName + "/blobFiles/";
var fileName = util.getNewUUID();
fileIO.mkdir(lobBasePath + relativeFolderPath, true);
fileIO.storeData(lobBasePath + relativeFolderPath + fileName, pValue, util.DATA_BINARY, false);
return this.appendIfNotNull(LiquiXmlUtils.colValue(pColName, relativeFolderPath + fileName, "valueBlobFile"));
};
LiquiXTable.prototype.addIdCol = function (pColName, pValue)
{
pValue = pValue.toString();
return this.appendIfNotNull(LiquiXmlUtils.colStr(pColName, LiquiXmlUtils._resolveId(pValue)));
};
LiquiXTable.prototype.addDateTimeCol = function (pColName, pValue)
{
return this.appendIfNotNull(LiquiXmlUtils.colDateTime(pColName, pValue));
};
LiquiXTable.prototype.addDateCol = function (pColName, pValue)
{
return this.appendIfNotNull(LiquiXmlUtils.colDateTime(pColName, pValue, "date"));
};
LiquiXTable.prototype.addNumberCol = function (pColName, pValue)
{
return this.appendIfNotNull(LiquiXmlUtils.colNumber(pColName, pValue));
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment