Module: DataMetaDom::MySqlLexer

Defined in:
lib/dataMetaDom/mySql.rb

Overview

Definition for generating MySQL 5 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 => 'smallint', 4 => 'int', 8 => 'bigint'}
FLOAT_TYPES =

Float types

{4 => 'float', 8 => 'double'}
NOT_NULL =

Not null (required) wording per MySQL DDL syntax

' not null'
SQL_TYPES =

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

{
        INT => lambda { |len, isReq|
            concreteType = INT_TYPES[len]
            raise "Invalid integer type length #{len} " unless concreteType
            "#{concreteType}#{isReq ? NOT_NULL : ''}"
        },
        DataMetaDom::FLOAT => lambda { |len, isReq|
            concreteType = FLOAT_TYPES[len]
            raise "Invalid integer type length #{len} " unless concreteType
            "#{concreteType}#{isReq ? NOT_NULL : ''}"
        },
        STRING => lambda { |len, isReq| "varchar(#{len})#{isReq ? NOT_NULL : ''}" },
        DATETIME => lambda { |len, isReq| "datetime#{isReq ? NOT_NULL : ''}" },
        BOOL => lambda { |len, isReq| "bool#{isReq ? NOT_NULL : ''}" }
}

Instance Method Summary collapse

Instance Method Details

#autoGenClauseIfAny(record, field) ⇒ Object

Builds and returns an autoincrement clause if applicable, for the given record and the field.

If the field is the one and only identity on the record and if it is an integral type, returns the auto increment clause, otherwise returns and empty string.



137
138
139
140
# File 'lib/dataMetaDom/mySql.rb', line 137

def autoGenClauseIfAny(record, field)
    record.identity && record.identity.length == 1 && field.name == record.identity[0] &&
            field.dataType.type == DataMetaDom::INT ? ' AUTO_INCREMENT' : ''
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.



182
183
184
# File 'lib/dataMetaDom/mySql.rb', line 182

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

#genDdl(parser, outDir) ⇒ Object

Generate the MySQL 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.



244
245
246
247
248
249
250
251
252
253
# File 'lib/dataMetaDom/mySql.rb', line 244

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

#renderField(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.



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/mySql.rb', line 151

def renderField(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, 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, field.isRequired
              else
                  raise ArgumentError, "Unsupported datatype #{ty}"
              end
    createStatement << ",\n" unless isFirstField
    createStatement << "\t#{field.name} #{typeDef}#{autoGenClauseIfAny(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.



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/dataMetaDom/mySql.rb', line 193

def renderRecord(out, parser, recordKey)
    record = parser.records[recordKey]
    ns, entityName = DataMetaDom.splitNameSpace record.name
    isFirstField = true
    out.drop.puts "\ndrop table if exists #{entityName};"
    fkNumber = 1 # to generate unique names that fit in 64 characters of identifier max length for MySQL
    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(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\tunique uq_#{entityName}_#{uqNumber}(#{uq.args.join(', ')})"
            uqNumber += 1 # to generate unique names that fit in 64 characters of identifier max length for MySQL
        }
    end
    unless record.indexes.empty?
        ixNumber = 1
        record.indexes.each_value { |ix|
            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 MySQL
        }
    end
    createStatement << "\n) Engine=InnoDB;\n\n" # MyISAM, the default engine does not support FKs

    out.create.puts createStatement
end