UNPKG

quick-ddl

Version:
1,106 lines (1,046 loc) 49.9 kB
var ddl= require('./ddl'); var singular= require('./singular').singular; var translate= require('./translate').translate; var sample= require('./sample').sample; var lexer = require('./lexer').lexer; var tree = (function(){ var tab= ' '; function ddlnode( x, inputLine, parent ) { this.x = x; this.y = function() { if( this.children.length == 0 ) return this.x+1; else return this.children[this.children.length-1].y(); }; this.parent = parent; this.children = []; if( parent != null ) parent.children.push(this); this.fks = null; this.pk = null; this.descendants = function() { var ret = []; ret.push(this); for( var i = 0; i < this.children.length; i++ ) ret = ret.concat(this.children[i].descendants()); return ret; } this.maxChildNameLen = function() { var maxLen = 2; var tmp = this.trimmedContent().toUpperCase(); if( ddl.optionEQvalue("Audit Columns",'yes') || 0 < tmp.indexOf('/AUDITCOLS') ) maxLen = 'updated_by'.length; if( ddl.optionEQvalue("Row Version Number",'yes') || 0 < tmp.indexOf('/ROWVERSION') ) maxLen = 'row_version'.length; if( this.fks != null ) for( var col in this.fks ) { //var parent = this.fks[col]; var len = col.length; if( maxLen < len ) maxLen = len; } for( var i = 0; i < this.children.length; i++ ) { if( 0 < this.children[i].children.length ) continue; var len = this.children[i].parseName().length; if( maxLen < len ) maxLen = len; } var cols = ddl.additionalColumns(); for( var col in cols ) { var len = col.length; if( maxLen < len ) maxLen = len; } return maxLen; } this.content = inputLine; this.comment; this.trimmedContent = function() { var ret = this.content.trim(); var start = ret.indexOf('['); var end = ret.indexOf(']'); if( this.comment == null && 0 < start ) this.comment = ret.substr(start+1, end-start-1); if( 0 < start ) { ret = ret.substr(0,start) + ret.substr(end+2); } var start = ret.indexOf('--'); if( this.comment == null && 0 < start ) this.comment = ret.substr(start+2); if( 0 < start ) { ret = ret.substr(0,start); } return ret.trim(); } this.parseName = function( prefix ) { function replaceTrailing ( ret, postfix ) { if( 0 < ret.indexOf(postfix) && ret.indexOf(postfix) == ret.length-postfix.length ) return ret.substring(0, ret.length-postfix.length); return ret; } var ret = this.trimmedContent(); var qtBegin = ret.indexOf('"'); var qtEnd = ret.indexOf('"', qtBegin+1); if( 0 <= qtBegin && qtBegin < qtEnd ) return ret.substring(qtBegin, qtEnd+1); ret = ret.toUpperCase(); if( 0 == ret.indexOf('VIEW ') ) { var chunks = ret.split(' '); return ddl.objPrefix() + chunks[1]; } ret = replaceTrailing(ret,' D'); var pos = ret.indexOf('/'); if( 0 < pos ) ret = ret.substring(0,pos); ret = ret.trim(); ret = replaceTrailing(ret,' INTEGER'); ret = replaceTrailing(ret,' NUMBER'); ret = replaceTrailing(ret,' INT'); ret = replaceTrailing(ret,' NUM'); ret = replaceTrailing(ret,' CLOB'); ret = replaceTrailing(ret,' BLOB'); ret = replaceTrailing(ret,' DATE'); ret = replaceTrailing(ret,' TSWTZ'); ret = replaceTrailing(ret,' TSWLTZ'); ret = replaceTrailing(ret,' TS'); ret = ret.replace(/ VC\d+/g,''); ret = ret.trim(); ret = ret.replace(/ /g,'_'); if( prefix == undefined ) if( 0 == this.children.length ) { // switched to this comparison stile because accidental typo this.children.length = 0 is disastrous! if( this.parent != undefined && this.parent.colprefix != undefined ) ret = this.parent.colprefix.toUpperCase()+'_' + ret; } else { ret = ddl.objPrefix() + ret; } var c = ret.substr(0,1); if (c >= '0' && c <= '9') { ret = 'x'+ret; } return ret; } this.parseType = function( pure ) { var tmp = this.trimmedContent().toUpperCase(); if( 0 == tmp.indexOf('--') ) return "COMMENT"; if( 0 == tmp.indexOf('VIEW ') ) return "VIEW"; var char = ddl.semantics(); var len = 4000; if( tmp.startsWith('NAME') || tmp.startsWith('EMAIL') ) len = 255; if( 0 < tmp.indexOf(' VC') ) { var start = tmp.indexOf(' VC'); var end = tmp.lastIndexOf(' '); if( end == start) end = tmp.length; len = parseInt(tmp.substr(start+' VC'.length, end-start-' VC'.length).trim()); } var ret = "VARCHAR2("+len+char+")"; if( pure == 'PLSQL' ) ret = "VARCHAR2"; if( 0 < tmp.indexOf(' INT') ) ret = "INTEGER"; if( 0 < tmp.indexOf('_ID') ) ret = "INTEGER"; if( 0 < tmp.indexOf(' NUM') /*|| 0 < tmp.indexOf('/PK')*/ ) ret = "NUMBER"; if( 0 == tmp.indexOf('DATE ') || 0 < tmp.indexOf(' DATE' ) || 0 < tmp.indexOf('_DATE') || 0 <= tmp.indexOf('HIREDATE') || 0 < tmp.indexOf(' D') && tmp.indexOf(' D') == tmp.length-' D'.length ) ret = ddl.getOptionValue("Date Data Type").toUpperCase(); if( 0 < tmp.indexOf(' CLOB') ) ret = "CLOB"; if( 0 < tmp.indexOf(' BLOB') ) ret = "BLOB"; if( 0 < tmp.indexOf(' TS') ) ret = "TIMESTAMP"; if( 0 < tmp.indexOf(' TSWTZ') ) ret = "TIMESTAMP WITH TIME ZONE"; if( 0 < tmp.indexOf(' TSWLTZ') ) ret = "TIMESTAMP WITH LOCAL TIME ZONE"; if( pure ) return ret; if( 0 < tmp.indexOf('/UNIQUE') ) { ret += '\n'; ret += tab + tab+' '.repeat(parent.maxChildNameLen()) +'constraint '+parent.parseName('noprefix=>true')+'_'+this.parseName('noprefix=>true')+'_unq unique'; } if( 0 <= tmp.indexOf('/NN') || 0 <= tmp.indexOf('/NOT NULL') ) ret += ' not null'; if( 0 <= tmp.indexOf('/HIDDEN') || 0 <= tmp.indexOf('/INVISIBLE') ) ret += ' invisible'; var optQuote = "'"; if( ret.startsWith('INTEGER') || ret.startsWith('NUMBER') || ret.startsWith('DATE') ) optQuote = ""; if( 0 <= tmp.indexOf('/DEFAULT') ) { var start = tmp.indexOf('/DEFAULT'); var end = tmp.lastIndexOf('/'); if( end == start) end = tmp.length; var value = tmp.substr(start+'/DEFAULT'.length, end-start-'/DEFAULT'.length).trim(); ret +=' default '+'on null ' + optQuote+value+optQuote ; } if( 0 <= tmp.indexOf('/CHECK') ) { var start = tmp.indexOf('/CHECK'); var end = tmp.lastIndexOf('/'); if( end == start) end = tmp.length; var values = tmp.substr(start+'/CHECK'.length, end-start-'/CHECK'.length).trim(); if( 0 < values.indexOf(', ') ) values = values.replace(/, /g,optQuote+","+optQuote).toUpperCase(); else if( 0 < values.indexOf(',') ) values = values.replace(/,/g,optQuote+","+optQuote).toUpperCase(); else values = values.replace(/ /g,optQuote+","+optQuote).toUpperCase(); ret +=' constraint '+parent.parseName()+'_'+this.parseName('noprefix=>true')+'_CK\n'; ret +=" check ("+this.parseName('noprefix=>true')+" in ("+optQuote+values+optQuote+"))"; } if( 0 <= tmp.indexOf('/BETWEEN') ) { var start = tmp.indexOf('/BETWEEN'); var end = tmp.lastIndexOf('/'); if( end == start) end = tmp.length; var values = tmp.substr(start+'/BETWEEN'.length, end-start-'/BETWEEN'.length).trim(); ret +=' constraint '+parent.parseName('noprefix=>true')+'_'+this.parseName('noprefix=>true')+'_BET\n'; ret +=" check ("+this.parseName('noprefix=>true')+" between "+values+")"; } if( 0 < tmp.indexOf('/PK') ) { if( this.parent != null ) this.parent.pk = this; let typeModifier = ' NOT NULL'; if( ret.startsWith('NUMBER') ) typeModifier = ' GENERATED BY DEFAULT ON NULL AS IDENTITY'; ret += typeModifier +'\n'; ret += tab + tab+' '.repeat(parent.maxChildNameLen()) +'constraint '+parent.parseName()+'_'+this.parseName('noprefix=>true')+'_pk primary key'; } return ret; } this.isExplicitPk = function() { for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; var tmp = child.trimmedContent().toUpperCase(); if( 0 < tmp.indexOf('/PK') ) return true; } return false; } this.refId = function() { var tmp = this.trimmedContent().toUpperCase(); var pos = tmp.indexOf(' ID '); if( 0 < pos ) return tmp.substr(0,pos)+'S'; pos = tmp.indexOf(' ID'); if( 0 < pos && pos == tmp.length-' ID'.length ) return tmp.substr(0,pos)+'S'; pos = tmp.indexOf('/FK'); if( 0 < pos ) return tmp.substr(pos+'/FK'.length).trim().replace(' ','_'); pos = tmp.indexOf('/REFERENCE'); if( 0 < pos ) return tmp.substr(pos+'/REFERENCE '.length).trim().replace(' ','_'); return null; } this.parseValues = function() { var tmp = this.trimmedContent().toUpperCase(); if( 0 <= tmp.indexOf('/CHECK') || 0 <= tmp.indexOf('/VALUES') ) { var substr = '/CHECK'; var start = tmp.indexOf(substr); if( start < 0 ) { substr = '/VALUES'; start = tmp.indexOf(substr); } var end = tmp.lastIndexOf('/'); if( end == start) end = tmp.length; var values = tmp.substr(start+substr.length, end-start-substr.length).trim(); if( 0 < values.indexOf(',') ) { values = values.replace(/ /g,""); return values.split(','); } else return values.split(' '); } if( 0 <= tmp.indexOf('/BETWEEN') ) { var start = tmp.indexOf('/BETWEEN'); var end = tmp.lastIndexOf('/'); if( end == start) end = tmp.length; var values = tmp.substr(start+'/BETWEEN'.length, end-start-'/BETWEEN'.length).trim(); values = values.replace(" AND "," "); var ret = []; for( var i = parseInt(values.split(' ')[0]); i <= parseInt(values.split(' ')[1]) ; i++ ) ret.push(i); return ret; } return null; } this.apparentDepth = function() { var chunks = this.content.split(/ |\t/); var offset = 0; for( var j = 0; j < chunks.length; j++ ) { var chunk = chunks[j]/*.intern()*/; //if( "\t" == chunk ) //TODO; if( "" == chunk ) { offset++; continue; } if( !/[^.a-zA-Z0-9_"]/.test(chunk) ) return offset; } throw 'No alphanumerics in the node content'; } this.depth = function() { if( this.parent == null ) return 0; return this.parent.depth()+1; } this.location = function() { return "["+x+","+this.y()+")"; } this.offset = function( padding ) { var s = ""; for( var i = 0; i < this.depth() ; i++ ) s = s + padding; return s; } this.toString = function( padding ) { return this.offset(padding) //+ nbrsp; + this.location() + padding + this.content ; } this.isLeaf = function( ) { return this.children.every((c) => c.children.length == 0); } this.toDDL = function( ) { if( this.parseType() == "VIEW" ) return ""; // postponed if( this.parseType() == "COMMENT" ) return this.content; if( this.children.length == 0 ) { let pad = tab; if( this.parent != undefined ) pad += ' '.repeat(this.parent.maxChildNameLen() - this.parseName().length); return this.parseName()+pad+this.parseType(); } if( this.fks == null ) { this.fks = []; if( this.parent != null ) this.fks[singular(this.parent.parseName('noprefix=>true'))+'_ID']=this.parent.parseName('noprefix=>true'); for( var i = 0; i < this.children.length; i++ ) if( this.children[i].refId() != null ) { this.fks[this.children[i].parseName()]=this.children[i].refId(); } } var postponed = []; var indexedColumns = []; var ret = ""; ret = ret + 'create table '+this.parseName()+" (\n"; var pad = tab+' '.repeat(this.maxChildNameLen() - 'ID'.length); if( !this.isExplicitPk() && ddl.optionEQvalue("Auto Primary Key",'yes') ) { let typeModifier = 'GENERATED BY DEFAULT ON NULL AS IDENTITY'; if( ddl.getOptionValue("Primary Key Maintenance") != ddl.identityDataType ) typeModifier = 'not null'; ret += tab + 'ID' + pad + 'NUMBER ' + typeModifier + '\n'; ret += tab + tab+' '.repeat(this.maxChildNameLen()) +'constraint '+this.parseName()+'_ID_PK primary key,\n'; } for( var fk in this.fks ) { let parent = this.fks[fk]; let type = 'NUMBER'; let refNode = ddl.find(parent); if( refNode != null && refNode.pk != null ) type = refNode.pk.parseType(pure=>true); pad = tab+' '.repeat(this.maxChildNameLen() - fk.length); ret += tab + fk + pad + type + '\n'; ret += tab + tab+' '.repeat(this.maxChildNameLen()) + 'constraint '+this.parseName(/*'noprefix=>true'*/)+'_'+fk+'_FK\n'; let onDelete = ' on delete '+ddl.getOptionValue("On Delete").toLowerCase(); if( ddl.optionEQvalue("On Delete",'Restrict') ) onDelete = ''; let notNull = ''; for( c in this.children ) { var child = this.children[c]; if( fk == child.parseName() ) { var tmp = child.trimmedContent().toUpperCase(); if( 0 <= tmp.indexOf('/NN') || 0 <= tmp.indexOf('/NOTNULL') ) notNull = ' NOT NULL' break; } } ret += tab + tab+' '.repeat(this.maxChildNameLen()) + 'references '+ddl.objPrefix()+parent+onDelete+notNull+',\n'; } for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; if( 0 < child.children.length ) { postponed.push(child); continue; } if (child.refId() == null ) { if( this.conflictsWithID(child) ) continue; //ret += '--'; ret += tab + child.toDDL([]) +',\n'; } } var tmp = this.trimmedContent().toUpperCase(); if( ddl.optionEQvalue("rowVersion",'yes') || 0 < tmp.indexOf('/ROWVERSION') ) { var pad = tab+' '.repeat(this.maxChildNameLen() - 'CREATED'.length); pad = tab+' '.repeat(this.maxChildNameLen() - 'ROW_VERSION'.length); ret += tab + 'ROW_VERSION' + pad + 'INTEGER not null,\n'; } if( ddl.optionEQvalue("Audit Columns",'yes') || 0 < tmp.indexOf('/AUDITCOLS') ) { var pad = tab+' '.repeat(this.maxChildNameLen() - 'CREATED'.length); ret += tab + 'CREATED' + pad + ddl.getOptionValue("Date Data Type").toUpperCase() + ' not null,\n'; pad = tab+' '.repeat(this.maxChildNameLen() - 'CREATED_BY'.length); ret += tab + 'CREATED_BY' + pad + 'VARCHAR2(255'+ddl.semantics()+') not null,\n'; pad = tab+' '.repeat(this.maxChildNameLen() - 'UPDATED'.length); ret += tab + 'UPDATED' + pad + ddl.getOptionValue("Date Data Type").toUpperCase() + ' not null,\n'; pad = tab+' '.repeat(this.maxChildNameLen() - 'UPDATED_BY'.length); ret += tab + 'UPDATED_BY' + pad + 'VARCHAR2(255'+ddl.semantics()+') not null,\n'; } var cols = ddl.additionalColumns(); for( var col in cols ) { var type = cols[col]; pad = tab+' '.repeat(this.maxChildNameLen() - col.length); ret += tab + col.toUpperCase() + pad + type + ' not null,\n'; } if( ret.lastIndexOf(',\n') == ret.length-2 ) ret = ret.substr(0,ret.length-2)+'\n'; ret += ')'+(ddl.optionEQvalue("Compression",'yes') || 0 < tmp.indexOf('/COMPRESS')?' compress':'')+';\n\n'; let num = 1; for( var fk in this.fks ) { var parent = this.fks[fk]; var ref = parent; if( parent.endsWith('S') ) ref = ref.substr(0,ref.length-1); var col = fk; if( col == null ) col = singular(ref)+'_ID'; ret += 'create index '+this.parseName()+'_i'+(num++)+' on '+this.parseName()+' ('+col+');\n\n'; } //ret += this.generateTrigger(); var j = 1; for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; var tmp = child.trimmedContent().toUpperCase(); if( 0 <= tmp.indexOf('/IDX') || 0 <= tmp.indexOf('/INDEX') ) ret += 'create index '+this.parseName()+'_i'+(num++)+' on '+this.parseName()+' ('+child.parseName()+');\n'; } if( this.comment != null ) ret += "comment on table "+this.parseName()+" is '"+this.comment+"';\n"; for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; if( child.comment != null && child.children.length == 0 ) ret += "comment on column "+this.parseName()+'.'+child.parseName()+" is '"+child.comment+"';\n"; } ret += '\n'; for( var i = 0; i < postponed.length; i++ ) { ret += postponed[i].toDDL(); } return ret; } this.generateDrop= function() { let ret = ""; if( this.parseType() == "VIEW" ) ret = 'drop view '+this.parseName()+";\n"; if( 0 < this.children.length ) { ret = 'drop table '+this.parseName()+" cascade constraints;\n"; if( ddl.optionEQvalue("api",'yes') ) ret+= 'drop package '+this.parseName()+"_api;\n"; } return ret; } this.generateView = function() { if( this.parseType() != "VIEW" ) return ""; if( ddl.optionEQvalue("Duality View",'yes') ) { return this.generateDualityView() } var tmp = this.trimmedContent().toUpperCase(); var chunks = tmp.split(' '); var ret = 'create or replace view ' +this.parseName()+ " as\n"; ret += 'select\n'; var maxLen = 0; for( var i = 2; i < chunks.length; i++ ) { var tbl = ddl.find(chunks[i]); if( tbl == null ) return ""; var len = (chunks[i]+'.ID').length; if( maxLen < len ) maxLen = len; for( var j = 0; j < tbl.children.length; j++ ) { var child = tbl.children[j]; len = (chunks[i]+'.'+child.parseName()).length; if( maxLen < len ) maxLen = len; } } var colCnts = {}; for( var i = 2; i < chunks.length; i++ ) { var tbl = ddl.find(chunks[i]); if( tbl == null ) continue; for( var j = 0; j < tbl.children.length; j++ ) { var child = tbl.children[j]; var col = child.parseName(); var cnt = colCnts[col]; if( cnt == null ) cnt = 0; colCnts[col] = cnt+1; } } for( var i = 2; i < chunks.length; i++ ) { var tbl = ddl.find(chunks[i]); if( tbl == null ) continue; var pad = ' '.repeat(maxLen - (chunks[i]+'.ID').length); ret += tab + chunks[i]+'.ID'+tab+pad+singular(chunks[i])+'_ID,\n'; for( var j = 0; j < tbl.children.length; j++ ) { var child = tbl.children[j]; if( 0 == child.children.length ) { pad = ' '.repeat(maxLen - (chunks[i]+'.'+child.parseName()).length); var disambiguator = ''; if( 1< colCnts[child.parseName()] ) disambiguator = singular(chunks[i])+'_'; ret += tab + chunks[i]+'.'+child.parseName()+tab+pad+disambiguator+child.parseName()+',\n'; } } var tmp = tbl.trimmedContent().toUpperCase(); if( ddl.optionEQvalue("rowVersion",'yes') || 0 < tmp.indexOf('/ROWVERSION') ) { var pad = tab+' '.repeat(tbl.maxChildNameLen() - 'ROW_VERSION'.length); ret += tab + chunks[i]+'.'+ 'ROW_VERSION' + pad + chunks[i]+'_'+ 'ROW_VERSION,\n'; } if( ddl.optionEQvalue("Audit Columns",'yes') || 0 < tmp.indexOf('/AUDITCOLS') ) { var pad = tab+' '.repeat(tbl.maxChildNameLen() - 'CREATED'.length); ret += tab + chunks[i]+'.'+ 'CREATED' + pad + chunks[i]+'_'+ 'CREATED,\n'; pad = tab+' '.repeat(tbl.maxChildNameLen() - 'CREATED_BY'.length); ret += tab + chunks[i]+'.'+ 'CREATED_BY' + pad + chunks[i]+'_'+ 'CREATED_BY,\n'; pad = tab+' '.repeat(tbl.maxChildNameLen() - 'UPDATED'.length); ret += tab + chunks[i]+'.'+ 'UPDATED' + pad + chunks[i]+'_'+ 'UPDATED,\n'; pad = tab+' '.repeat(tbl.maxChildNameLen() - 'UPDATED_BY'.length); ret += tab + chunks[i]+'.'+ 'UPDATED_BY' + pad + chunks[i]+'_'+ 'UPDATED_BY,\n'; } } if( ret.lastIndexOf(',\n') == ret.length-2 ) ret = ret.substr(0,ret.length-2)+'\n'; ret += 'from\n'; for( var i = 2; i < chunks.length; i++ ) { pad = ' '.repeat(maxLen - chunks[i].length); var tbl = chunks[i]; if( ddl.objPrefix() != null && ddl.objPrefix() != '' ) ddl.objPrefix()+chunks[i] + pad + chunks[i]; ret += tab + tbl + ',\n'; } if( ret.lastIndexOf(',\n') == ret.length-2 ) ret = ret.substr(0,ret.length-2)+'\n'; ret += 'where\n'; for( var i = 2; i < chunks.length; i++ ) for( var j = 2; j < chunks.length; j++ ) { if( j == i ) continue; var nameA = chunks[i]; var nameB = chunks[j]; var nodeA = ddl.find(nameA); if( nodeA == null ) continue; var nodeB = ddl.find(nameB); if( nodeB == null ) continue; for( var k in nodeA.fks ) { var parent = nodeA.fks[k]; if( parent == nameB) { ret += tab + nameA+'.'+singular(parent)+'_ID(+) = ' +nameB+'.ID and\n'; } } } if( ret.lastIndexOf(' and\n') == ret.length-' and\n'.length ) ret = ret.substr(0,ret.length-' and\n'.length)+'\n'; ret += '/\n'; return ret; } this.generateTrigger = function() { if( this.children.length == 0 ) return ""; var ret = 'create or replace trigger '+ this.parseName() +'_BIU\n'; ret += ' before insert or update\n'; ret += ' on '+ this.parseName() +'\n'; ret += ' for each row\n'; ret += 'begin\n'; var OK = false; var user = 'user'; if( ddl.optionEQvalue("apex",'yes') ) { user = "coalesce(sys_context('APEX$SESSION','APP_USER'),user)"; } for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; method = null; if( 0 < child.content.indexOf('/lower') ) method = 'LOWER'; else if( 0 < child.content.indexOf('/upper') ) method = 'UPPER'; if( method == null ) continue; ret += ' :new.'+child.parseName()+' := '+method+'(:new.'+child.parseName()+');\n'; OK = true; } var tmp = this.trimmedContent().toUpperCase(); if( ddl.optionEQvalue("Row Version Number",'yes') || 0 < tmp.indexOf('/ROWVERSION') ) { ret += ' if inserting then\n'; ret += ' :new.row_version := 1;\n'; ret += ' elsif updating then\n'; ret += ' :new.row_version := NVL(:old.row_version, 0) + 1;\n'; ret += ' end if;\n'; OK = true; } if( ddl.optionEQvalue("Audit Columns",'yes') || 0 < tmp.indexOf('/AUDITCOLS') ) { ret += ' if inserting then\n'; ret += ' :new.created := SYSDATE;\n'; ret += ' :new.created_by := '+user+';\n'; ret += ' end if;\n'; ret += ' :new.updated := SYSDATE;\n'; ret += ' :new.updated_by := '+user+';\n'; OK = true; } if( ddl.optionEQvalue("Auto Primary Key",'yes') && ddl.getOptionValue("Primary Key Maintenance") != ddl.identityDataType ) { ret += ' if :new.id is null then\n'; ret += " :new.id := to_number(sys_guid(), 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');\n"; ret += ' end if;\n'; OK = true; } var cols = ddl.additionalColumns(); for( var col in cols ) { var type = cols[col]; ret += ' if :new.'+col+' is null then\n'; if( type.startsWith('INT') ) ret += ' '+col+' := 0;\n'; else ret += ' '+col+" := 'N/A';\n"; ret += ' end if;\n'; OK = true; } if( !OK ) return ''; ret += 'end ' + this.parseName() + '_BIU;\n/\n\n'; return ret; } this.procDecl = function( kind /* get, insert, update */ ) { let modifier = ''; if( kind != 'get' ) modifier = ' default null'; let mode = 'out'; if( kind != 'get' ) mode = ' in'; let ret = tab+'procedure '+kind+'_row (\n'; ret += tab+tab+'p_id in number'+modifier; for( var fk in this.fks ) { let parent = this.fks[fk]; let type = 'NUMBER'; let refNode = ddl.find(parent); if( refNode != null && refNode.pk != null ) type = refNode.pk.parseType(pure=>true); //pad = tab+tab+' '.repeat(this.maxChildNameLen() - fk.length); ret += ',\n'; ret += tab+tab+'P_'+fk+' '+mode+' '+type+modifier; } for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; if( child.children.length != 0 ) continue; ret += ',\n'; ret += tab+tab+'P_'+child.parseName()+' '+mode+' '+child.parseType('PLSQL')+modifier; } ret += '\n )'; return ret; } this.procBody = function( kind /* get, insert, update */ ) { let ret = tab+'is \n'; ret += tab+'begin \n'; let prelude = tab+tab+'for c1 in (select * from '+this.parseName()+' where id = p_id) loop \n'; if( kind == 'insert' ) { prelude = tab+tab+'insert into '+this.parseName()+' ( \n'; prelude += tab+tab+'id'; } if( kind == 'update' ) { prelude = tab+tab+'update '+this.parseName()+' set \n'; prelude += tab+tab+'id = p_id'; } ret += prelude; for( var fk in this.fks ) { let parent = this.fks[fk]; let type = 'NUMBER'; let refNode = ddl.find(parent); if( refNode != null && refNode.pk != null ) type = refNode.pk.parseType(pure=>true); //pad = tab+tab+' '.repeat(this.maxChildNameLen() - fk.length); if( kind == 'insert' || kind == 'update' ) ret += ',\n'; let row = ' P_'+fk+' := c1.'+fk+';\n'; if( kind == 'insert' ) row = ' '+fk; if( kind == 'update' ) row = ' '+fk+' = P_'+fk+'\n'; ret += row; } for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; if( child.children.length != 0 ) continue; if( kind == 'insert' || kind == 'update' ) ret += ',\n'; let row = ' P_'+child.parseName()+' := c1.'+child.parseName()+';\n'; if( kind == 'insert' ) row = ' '+child.parseName(); if( kind == 'update' ) row = ' '+child.parseName()+' = P_'+child.parseName()+'\n'; ret += row; } if( kind == 'insert' ) { ret += ' ) values ( \n'; ret += ' p_id'; for( var fk in this.fks ) { ret += ',\n'; ret += ' P_'+fk; } for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; if( child.children.length != 0 ) continue; ret += ',\n'; ret += ' P_'+child.parseName(); } } let finale = '\n end loop;\n'; if( kind == 'insert' ) finale = ' );' if( kind == 'update' ) finale = ' where id = p_id;' ret += finale; ret += ' end '+kind+'_row;\n '; ret += '\n '; return ret; } this.generateTAPI = function() { if( this.children.length == 0 ) return ""; var ret = 'create or replace package '+ this.parseName() +'_API\nis\n\n'; ret += this.procDecl('get'); ret += ';\n\n'; ret += this.procDecl('insert'); ret += ';\n\n'; ret += this.procDecl('update'); ret += ';\n\n'; ret += ' procedure delete_row (\n'+ ' p_id in number\n'+ ' );\n'+ 'end '+this.parseName()+'_api;\n'+ '/\n\n'; ret += 'create or replace package body '+ this.parseName() +'_API\nis\n\n'; ret += this.procDecl('get'); ret += '\n'; ret += this.procBody('get'); ret += this.procDecl('insert'); ret += '\n'; ret += this.procBody('insert'); ret += this.procDecl('update'); ret += '\n'; ret += this.procBody('update'); ret += ' procedure delete_row (\n' ret += ' p_id in number\n' ret += ' )\n' ret += ' is\n' ret += ' begin\n' ret += ' delete from '+this.parseName()+' where id = p_id;\n' ret += ' end delete_row;\n' ret += 'end '+this.parseName()+'_api;\n' ret += '/\n'; return ret; } this.rows = 0; this.generateData = function() { var ret = ''; var tmp = this.trimmedContent(); var start = tmp.indexOf('/insert '); if( 0 < start ) { tmp = tmp.substr(start+'/insert '.length); var tmps = tmp.split(' '); this.rows = parseInt(tmps[0]); if( 300 < this.rows ) this.rows = 300; for( var i = 0; i < this.rows; i++ ) { ret += 'insert into '+this.parseName()+' (\n'; if( /*!this.isExplicitPk() &&*/ ddl.optionEQvalue("Auto Primary Key",'yes') ) { ret += tab + 'ID,\n'; } for( var fk in this.fks ) { var parent = this.fks[fk]; ret += ' '+singular(parent)+'_ID,\n'; } for( var j = 0; j < this.children.length; j++ ) { var child = this.children[j]; if( 0 == child.children.length ) ret += ' '+child.parseName()+',\n'; } if( ret.lastIndexOf(',\n') == ret.length-2 ) ret = ret.substr(0,ret.length-2)+'\n'; ret += ') values (\n'; if( /*!this.isExplicitPk() &&*/ ddl.optionEQvalue("Auto Primary Key",'yes') ) { ret += tab + (i+1)+ ',\n'; } for( var fk in this.fks ) { var parent = this.fks[fk]; var ref = parent; if( parent.endsWith('S') ) ref = ref.substr(0,ref.length-1); var refNode = ddl.find(parent); var values = []; for( var k = 1; k <= refNode.rows ; k++ ) values.push(k); ret += ' '+translate(ddl.getOptionValue("Data Language"),sample(this.parseName(),singular(ref)+'_ID', 'INTEGER', values))+',\n'; } for( var j = 0; j < this.children.length; j++ ) { var child = this.children[j]; if( 0 == child.children.length ) ret += ' '+translate(ddl.getOptionValue("Data Language"),sample(this.parseName(),child.parseName(), child.parseType(), child.parseValues()))+',\n'; } if( ret.lastIndexOf(',\n') == ret.length-2 ) ret = ret.substr(0,ret.length-2)+'\n'; ret += ');\n'; } ret += '\n'; } if( ret == '' ) return ret; ret += 'commit;\n\n'; if( /*!this.isExplicitPk() &&*/ ddl.optionEQvalue("Auto Primary Key",'yes') ) { ret += 'alter table '+this.parseName()+'\n' + 'modify id generated '+'always '/*'by default on null'*/+' as identity restart start with '+(i+1)+";"; } for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; if( 0 < child.children.length ) ret += child.generateData(); } return ret; } this.isArray = function( ) { if (this.content.includes('/array')) return true; var insert = this.content.indexOf('/insert '); if (0 < insert) { var tokens = this.content.substr(insert).split(/\s+/); return parseInt(tokens[1]) > 1; } return false; } this.hasNonArrayChildId = function( cname ) { if(!cname.endsWith('_ID')) return false; var name = cname.slice(0, -3); return this.children.some((c) => c.children.length > 0 && c.parseName('noprefix=>true') == name && !c.isArray()); } this.generateSelectJsonBottomUp = function( tbl, indent) { var name = tbl.parseName('noprefix=>true'); var ret = indent + "'" + getRefId(name) + "' : " + name +'.ID,\n'; for( var j = 0; j < tbl.children.length; j++ ) { var child = tbl.children[j]; var cname = child.parseName('noprefix=>true'); if( 0 == child.children.length ) { ret += indent + "'" + cname + "' : " + name + '.' + cname + ',\n'; } } var ptbl = tbl.parent; if( ptbl != null ) { var pname = ptbl.parseName('noprefix=>true'); ret += indent + "'" + pname + "' : (\n"; indent += ' '; ret += indent + 'select JSON {\n'; ret += this.generateSelectJsonBottomUp( ptbl, indent + ' '); ret += indent + '} from ' + ptbl.parseName() + ' ' + pname + ' with (UPDATE)\n'; ret += indent + 'where ' + name + '.' + pname + '_ID = ' + pname + '.ID\n'; indent = indent.slice(0, -2); ret += indent + ')\n'; } else { ret = ret.slice(0, -2) + '\n'; } return ret; } this.generateSelectJsonTopDown = function( tbl, indent) { var name = tbl.parseName('noprefix=>true'); var ret = indent + "'" + getRefId(name) + "' : " + name + '.ID,\n'; for( var j = 0; j < tbl.children.length; j++ ) { var child = tbl.children[j]; var cname = child.parseName('noprefix=>true'); if( 0 == child.children.length ) { if (tbl.hasNonArrayChildId(cname)) continue; ret += indent + "'" + cname + "' : " + name + '.' + cname; } else { ret += indent + "'" + cname + "' : (\n"; var isArray = child.isArray(); indent += ' '; ret += indent + 'select ' + (isArray?'JSON_ARRAYAGG(':'') + 'JSON {\n'; ret += this.generateSelectJsonTopDown(child, indent + ' '); ret += indent + '}' + (isArray?')':'') + ' from ' + child.parseName() + ' ' + cname + ' with (UPDATE)\n'; var names = isArray? [name, cname] : [cname, name]; ret += indent + 'where ' + names[1] + '.' + getRefId(names[0]) + ' = ' + names[0] + '.ID\n'; indent = indent.slice(0, -2); ret += indent + ')'; } ret += (j < tbl.children.length - 1)? ',\n' : '\n'; } return (ret[ret.length-2] == ',')? ret.slice(0, -2) + '\n' : ret; } this.generateDualityView = function( ) { var tmp = this.trimmedContent().toUpperCase(); var chunks = tmp.split(' '); var ret = ''; var tbl = ddl.find(chunks[2]); if( tbl != null) { ret += 'create or replace json relational duality view ' + chunks[1] + ' as\n'; ret += 'select JSON {\n'; ret += tbl.isLeaf()? this.generateSelectJsonBottomUp(tbl, ' ') : this.generateSelectJsonTopDown(tbl, ' '); ret += '} from ' + tbl.parseName() + ' ' + chunks[2] + ' with (INSERT, UPDATE, DELETE);\n\n'; } return ret; } render = function( table, depth ) { var row = table.insertRow(-1); var cell = row.insertCell(0); var nbrsp = String.fromCharCode(160); var nbrspX3 = nbrsp+nbrsp+nbrsp; //cell.innerHTML = this.toString(apparentDepth, nbrspX3); //<---- no sugarcoating cell.innerHTML = "<font face=\"Lucida Console\" size=-1>" +this.offset(nbrspX3) + "<font color=blue face=\"Lucida Console\" size=-1>" + this.location() + nbrsp + "</font><font color=DarkMagenta face=\"Lucida Console\" size=-1>" + this.content.trim() ; for( var i = 0; i < this.children.length; i++ ) { var child = this.children[i]; child.render(table, depth+1); } } this.conflictsWithID = function (child) { if( ddl.getOptionValue("Auto Primary Key") != 'yes' ) return false; const upperName = child.parseName('noprefix=>true').toUpperCase(); if( 'ID' == upperName ) return true; var table = singular(this.parseName('noprefix=>true').toUpperCase()); if( table+'_ID' == upperName ) return true; return false; } } function getRefId( name ) { return singular(name.toUpperCase()) + "_ID"; } function recognize( input ) { if( ddl.ddl != null ) ddl = ddl.ddl; var ret = []; var path = []; var lines = input.split("\n"); var lineNo = -1; for( var i = 0; i < lines.length; i++ ) { var line = lines[i]; if( "\n" == line.trim() ) continue; if( line.trim().startsWith('--') ) continue; if( line.trim().startsWith('#') ) { for( var j = i+1; j < lines.length; j++ ) { var l = lines[j]; if( "\n" == l.trim() ) { i = j; break; } if( l.startsWith('--') ) { i = j; break; } if( l.startsWith('#') ) { break; } line += " "+l; i = j; if( l.trim().endsWith('}') ) { break; } } setOptions(line); continue; } lineNo++; line = line.replace(/\r/g,''); var nc = line.replace(/\r/g,'').replace(/ /g,''); //if( /[^a-zA-Z0-9="{}\/.,_\-\[\]]/.test(nc) ) //continue; if( "" == nc ) continue; var node = new ddlnode(lineNo,line,null); // node not attached to anything var matched = false; for( var j = 0; j < path.length; j++ ) { var cmp = path[j]; if( node.apparentDepth() <= cmp.apparentDepth() ) { if( 0 < j ) { var parent = path[j-1]; node = new ddlnode(lineNo,line,parent); // attach node to parent path[j] = node; path = path.slice(0, j+1); matched = true; break; } else { path[0] = node; path = path.slice(0, 1); ret.push(node); matched = true; } } } if( !matched ) { if( 0 < path.length ) { var parent = path[path.length-1]; node = new ddlnode(lineNo,line,parent); } path.push(node); if( node.apparentDepth() == 0 ) ret.push(node); } } return ret; } function setOptions( line ) { if( line.startsWith('#') ) line = line.substring(1).trim(); const eqPos = line.indexOf('='); let tmp = line.substring(eqPos + 1).trim(); if( tmp.indexOf('{') < 0 ) { tmp = '{' + line + '}'; } let json = ""; let src= lexer( tmp, true, true, "" ); for( i in src ) { let t = src[i]; if( t.type == "identifier" && t.value != "true" && t.value != "false" && t.value != "null" ) json += '"'+t.value+'"'; else json += t.value; } let settings = JSON.parse(json); for( let x in settings ) { ddl.setOptionValue(x.toLowerCase(),settings[x]); } } return recognize; }()); exports.tree = tree;