Module: DataMetaDom::OraLexer

Defined in:
lib/dataMetaDom/ora.rb

Overview

Definition for generating Oracle 11 and later artifacts such as schemas, select statements, ORM input files etc etc

TODO this isn’t a bad way, but beter use templating next time such as ERB.

For command line details either check the new method’s source or the README.rdoc file, the usage section.

Defined Under Namespace

Classes: SqlOutput

Constant Summary collapse

INT_TYPES =

Integer types

{2 => 'number(6)', 4 => 'number(9)', 8 => 'number(38)'}
NOT_NULL =

Not null (required) wording per Oracle DDL syntax

' not null'
SQL_TYPES =

Mapping from DataMeta DOM standard types to correspondent Oracle types renderer lambdas.

{
        INT => lambda { |len, scale, isReq|
            concreteType = INT_TYPES[len]
            raise "Invalid integer type length #{len} " unless concreteType
            "#{concreteType}#{isReq ? NOT_NULL : ''}"
        },
        STRING => lambda { |len, scale, isReq| "nvarchar2(#{len})#{isReq ? NOT_NULL : ''}" },
        DATETIME => lambda { |len, scale, isReq| "timestamp#{isReq ? NOT_NULL : ''}" },
        # Boolean implementation in Oracle is not optimal, see the doc:
        # http://docs.oracle.com/cd/B19306_01/olap.102/b14346/dml_datatypes004.htm
        BOOL => lambda { |len, scale, isReq| "boolean#{isReq ? NOT_NULL : ''}" },
        CHAR => ->(len, scale, isReq) { "nchar(#{len})#{isReq ? NOT_NULL : ''}" },
        NUMERIC => ->(len, scale, isReq) { "number(#{len}, #{scale})#{isReq ? NOT_NULL : ''}" }

}

Instance Method Summary collapse

Instance Method Details

#autoGenClauseIfAny(out, record, field) ⇒ Object

Renders autoincrement the very special Oracle way, via a sequence FIXME: need to check the auto hint

Returns:

  • empty string



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/dataMetaDom/ora.rb', line 142

def autoGenClauseIfAny(out, record, field)
    if record.identity && record.identity.length == 1 && field.name == record.identity[0] &&
            field.dataType.type == DataMetaDom::INT
        ns, entityName = DataMetaDom.splitNameSpace record.name
        seqName = "#{entityName}_#{field.name}_sq"
# Transaction separators are important for triggers and sequences
        out.crSeqs.puts %|
CREATE SEQUENCE #{seqName};

/
|
=begin rdoc
 To make the sequence used automatically:
CREATE OR REPLACE TRIGGER #{entityName}_#{field.name}_trg
BEFORE INSERT ON #{entityName}
FOR EACH ROW

BEGIN
  SELECT #{seqName}.NEXTVAL
  INTO   :new.#{field.name}
  FROM   dual;
END;
=end
        out.drSeqs.puts %|

drop SEQUENCE #{seqName};
/
|
    end

    ''
end

#fkName(bareEntityName, index) ⇒ Object

Builds and returns the foreign key name for the given entity (Record) name and the counting number of these.

  • Parameters:

    • bareEntityName - the entity name without the namespace

    • index - an integer, an enumerated counting number, starting from one. For each subsequent FK this number is incremented.



213
214
215
# File 'lib/dataMetaDom/ora.rb', line 213

def fkName(bareEntityName, index)
    "fk_#{bareEntityName}_#{index}"
end

#genDdl(parser, outDir) ⇒ Object

Generate the Oracle DDL from the given Model into the given output directory.

  • Parameters

    • parser - an instance of a Model

    • outDir - a String, the directory to generate the DDL into.



283
284
285
286
287
288
289
290
291
292
# File 'lib/dataMetaDom/ora.rb', line 283

def genDdl(parser, outDir)
    out = SqlOutput.new(outDir)
    begin
        parser.records.each_key { |r|
            renderRecord(out, parser, r)
        }
    ensure
        out.close
    end
end

#renderField(out, createStatement, parser, record, fieldKey, isFirstField) ⇒ Object

Renders the given field into create statement.

  • Parameters:

    • createStatement - the create statement to append the field definition to.

    • parser - the instance of the Model

    • record - the instance of the Record to which the field belongs

    • fieldKey - the full name of the field to render turned into a symbol.

    • isFirstField - the boolean, true if the field is first in the create statement.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/dataMetaDom/ora.rb', line 184

def renderField(out, createStatement, parser, record, fieldKey, isFirstField)
    field = record[fieldKey]
    ty = field.dataType
    stdRenderer = SQL_TYPES[ty.type]
    typeEnum = parser.enums[ty.type]
    typeRec = parser.records[ty.type]

    typeDef = if stdRenderer
                  stdRenderer.call ty.length, ty.scale, field.isRequired
              elsif typeEnum
                  "enum('#{typeEnum.values.join("','")}')"
              elsif typeRec
                  raise "Invalid ref to #{typeRec} - it has no singular ID" unless typeRec.identity.length == 1
                  idField = typeRec[typeRec.identity[0]]
                  idRenderer = SQL_TYPES[idField.dataType.type]
                  raise 'Only one-level prim type references only allowed in this version' unless idRenderer
                  idRenderer.call idField.dataType.length, idField.dataType.scale, field.isRequired
              end
    createStatement << ",\n" unless isFirstField
    createStatement << "\t#{field.name} #{typeDef}#{autoGenClauseIfAny(out, record, field)}"
end

#renderRecord(out, parser, recordKey) ⇒ Object

Render SQL record with for the given model into the given output.

  • Parameters

    • out - an instance of SqlOutput

    • parser - an instance of Model

    • recordKey - full name of the record datatype including namespeace if any turned into a symbol.



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/dataMetaDom/ora.rb', line 224

def renderRecord(out, parser, recordKey)
    record = parser.records[recordKey]
    ns, entityName = DataMetaDom.splitNameSpace record.name
    isFirstField = true
    # Oracle does not have neatly defined feature of dropping table if it exists
    # https://community.oracle.com/thread/2421779?tstart=0
    out.drop.puts %|\ndrop table #{entityName};
/
|
    fkNumber = 1 # to generate unique names that fit in 64 characters of identifier max length for Oracle
    record.refs.select { |r| r.type == Reference::RECORD }.each { |ref|
        ns, fromEntityBareName = DataMetaDom.splitNameSpace ref.fromEntity.name
        ns, toEntityBareName = DataMetaDom.splitNameSpace ref.toEntity.name
        out.couple.puts "alter table #{fromEntityBareName} add constraint #{fkName(fromEntityBareName, fkNumber)} "\
  " foreign key (#{ref.fromField.name}) references #{toEntityBareName}(#{ref.toFields.name});"
        out.uncouple.puts "alter table #{fromEntityBareName} drop foreign key #{fkName(fromEntityBareName, fkNumber)};"
        fkNumber += 1
    }
    ids = record.identity ? record.identity.args : []
    createStatement = "create table #{entityName} (\n"
    fieldKeys = [] << ids.map { |i| i.to_s }.sort.map { |i| i.to_sym } \
   << record.fields.keys.select { |k| !ids.include?(k) }.map { |k| k.to_s }.sort.map { |k| k.to_sym }

    fieldKeys.flatten.each { |f|
        renderField(out, createStatement, parser, record, f, isFirstField)
        isFirstField = false
    }
    if record.identity && record.identity.length > 0
        createStatement << ",\n\tprimary key(#{ids.sort.join(', ')})"
    end
    unless record.uniques.empty?
        uqNumber = 1
        record.uniques.each_value { |uq|
            createStatement << ",\n\tconstraint uq_#{entityName}_#{uqNumber} unique(#{uq.args.join(', ')})"
            uqNumber += 1 # to generate unique names that fit in 30 characters of identifier max length for Oracle
        }
    end
    unless record.indexes.empty?
        ixNumber = 1
        record.indexes.each_value { |ix|
            out.ixes.puts %|
CREATE INDEX #{entityName}_#{ixNumber} ON #{entityName}(#{ix.args.join(', ')});
/
|
#                createStatement << ",\n\tindex ix_#{entityName}_#{ixNumber}(#{ix.args.join(', ')})"
            ixNumber += 1 # to generate unique names that fit in 64 characters of identifier max length for Oracle
        }
    end
    createStatement << "\n);\n/\n"

    out.create.puts createStatement
end