
// // Copyright (c) 2008, John Sublett // Licensed under the Academic Free License version 3.0 // // History: // 14 Jan 08 John Sublett Creation // using sql ** ** ColDef defines the mapping of a field in a Type ** to a column in a relational table. ** @serializable abstract const class ColDef { ** ** Protected no argument constructor for subclasses. ** protected new make() { } ** ** Make a new instance for the specified field. ** protected new makeForField(HavenService haven, Field field) { this.haven = haven this.fieldName = field.qname } ** ** Validate the field for this column from the ** default row. ** virtual Void validate(Obj defaultRow) { // make sure the column has a default value if it // does not allow null if (!allowNull && !isRef) { def := field.get(defaultRow) if (def == null) throw HavenErr.make ("Default value required for fields that do not allow null. ($field.qname)") } } ** ** Get the database column name. ** virtual Str columnName() { return field.name } ** ** Get the definition of the column as it appears in a CREATE TABLE ** statement. ** virtual Str definition() { pk := isKey dialect := haven.dialect s := StrBuf.make(64) s.add(columnName).add(" ").add(sqlType) if (!pk && isUnique) s.add(" ").add(dialect.unique()) if (!allowNull) s.add(" ").add(dialect.notNull()) if (isAuto) s.add(" ").add(dialect.auto()) return s.toStr } ** ** Convert the field value of this column for the specified object ** to SQL text. ** Str fieldToSql(Obj o) { fieldVal := field.get(o) if (fieldVal == null) return "NULL" else return toSql(fieldVal) } ** ** Set the field for this column on the specified object by ** mapping the field value from the specified row. ** abstract Void setFieldFromRow(Obj obj, Row row, HavenNamespace ns) ** ** Get a value for this column type from the specified row. ** This is used by ref columns to read their database values. ** abstract Obj rowValue(Row row, Col col) ** ** Convert an object to its equivalent SQL syntax. ** abstract Str toSql(Obj o) ** ** Add a translated parameter or parameters to the specified ** map for use in a prepared statement. ** virtual Obj paramValue(Obj o, Str:Obj params := null) { if (params != null) params[field.name] = o return o } ** ** Get the SQL type definition for this translator ** abstract Str sqlType() ** ** Get the column name. ** Str name() { return field.name } ** ** Is this column part of the primary key? ** Bool isKey() { return field.facet("key", false) } ** ** Is this column value auto-generated? ** Bool isAuto() { return field.facet("auto", false) } ** ** Is this column a foreign key to another table? ** Bool isRef() { return (field.of == Uri.type) && (field.facet("ref") != null) } ** ** Is each value in this column unique? ** Bool isUnique() { return field.facet("unique", false) } ** ** Does this column allow null values to be persisted? ** Bool allowNull() { return field.facet("allowNull", false); } ** ** Is this column indexed? ** Bool isIndexed() { return field.facet("index", false) } ** ** Get the index definition for this column. ** If the column is not indexed, null is returned. ** IndexDef index() { fv := field.facet("index", false); if (fv.type == Bool.type) { if (!fv as Bool) return null return IndexDef.make( haven, haven.indexName(field.parent, field.name), [this]) } else if (fv.type == Str.type) { sv := fv as Str if (sv == "desc") return IndexDef.make( haven, haven.indexName(field.parent, field.name), [this], true) else { elems := sv.split(" ") if (elems.size == 1) return IndexDef.make( haven, haven.indexName(field.parent, elems[0]), [this]) else { if (elems[1] == "desc") return IndexDef.make( haven, haven.indexName(field.parent, elems[0]), [this], true) else throw HavenErr.make("Invalid index facet: $sv") } } } else throw HavenErr.make("index facet must be a boolean or a string.") } ////////////////////////////////////////////////////////////////////////// // Fields ////////////////////////////////////////////////////////////////////////// ** ** The haven used by this column. ** const HavenService haven ** ** The field that this column represents. This field ** may be lazily loaded from the field qname if this ** column definition has been serialized. ** const Str fieldName Field field() { return Slot.findField(fieldName) } }