in calcite-adapter/src/main/java/software/amazon/documentdb/jdbc/metadata/DocumentDbTableSchemaGenerator.java [124:234]
private static void processDocument(
final BsonDocument document,
final Map<String, DocumentDbSchemaTable> tableMap,
final List<DocumentDbMetadataColumn> foreignKeys,
final String path,
final String collectionName,
final boolean isRootDocument,
final Map<String, String> tableNameMap) {
// Need to preserve order of fields.
final LinkedHashMap<String, DocumentDbSchemaColumn> columnMap = new LinkedHashMap<>();
final String tableName = toName(combinePath(collectionName, path), tableNameMap);
if (tableMap.containsKey(tableName)) {
// If we've already visited this document/table,
// start with the previously discovered columns.
// This will have included and primary/foreign key definitions.
columnMap.putAll(tableMap.get(tableName).getColumnMap());
} else {
// Add foreign keys.
//
// Foreign key(s) are the primary key(s) passed from the parent table.
// Minimally, this is the primary key for the "_id" field.
//
// If called from an array parent, it will also include the "index_lvl_<n>"
// column(s) from the previous level in the array.
//
// The primaryKeyColumn and foreignKeyColumn are the one-indexed value
// referencing the order withing the primary or foreign key column.
int primaryKeyColumn = KEY_COLUMN_NONE;
for (DocumentDbMetadataColumn column : foreignKeys) {
primaryKeyColumn++;
buildForeignKeysFromDocument(columnMap, tableName, primaryKeyColumn, column);
}
}
final Map<String, String> columnNameMap = columnMap.values().stream().collect(
Collectors.toMap(
DocumentDbSchemaColumn::getSqlName,
DocumentDbSchemaColumn::getSqlName));
// Process all fields in the document
for (Entry<String, BsonValue> entry : document.entrySet()) {
final String fieldName = entry.getKey();
final String fieldPath = combinePath(path, fieldName);
final BsonValue bsonValue = entry.getValue();
final BsonType bsonType = bsonValue.getBsonType();
final boolean isPrimaryKey = isRootDocument && isIdField(fieldName);
final String columnName = getFieldNameIfIsPrimaryKey(
collectionName, fieldName, isPrimaryKey, columnNameMap);
final DocumentDbMetadataColumn prevMetadataColumn = (DocumentDbMetadataColumn) columnMap
.getOrDefault(columnName, null);
// ASSUMPTION: relying on the behaviour that the "_id" field will ALWAYS be first
// in the root document.
final JdbcType prevSqlType = getPrevSqlTypeOrDefault(prevMetadataColumn);
final JdbcType nextSqlType = getSqlTypeIfIsPrimaryKey(bsonType, prevSqlType, isPrimaryKey);
if (LOGGER.isDebugEnabled()) {
final JdbcType currentDocType = getSqlTypeIfIsPrimaryKey(bsonType, JdbcType.NULL, isPrimaryKey);
if (!prevSqlType.equals(currentDocType) && prevMetadataColumn != null) {
LOGGER.debug(String.format("Type conflict in table %s, types %s and %s mapped to %s.",
tableName, prevSqlType.name(), currentDocType, nextSqlType.name()));
}
}
processComplexTypes(
tableMap,
new ArrayList<>(foreignKeys),
collectionName,
entry,
fieldPath,
bsonType,
prevMetadataColumn,
nextSqlType,
tableNameMap);
final DocumentDbMetadataColumn metadataColumn = DocumentDbMetadataColumn
.builder()
.fieldPath(fieldPath)
.sqlName(columnName)
.sqlType(nextSqlType)
.dbType(getPromotedBsonType(bsonType, prevMetadataColumn))
.isIndex(false)
.isPrimaryKey(isPrimaryKey)
.index(getPrevIndexOrDefault(prevMetadataColumn, columnMap.size() + 1))
.tableName(tableName)
.primaryKeyIndex(getPrimaryKeyColumn(isPrimaryKey))
.foreignKeyIndex(KEY_COLUMN_NONE)
.isGenerated(false)
.virtualTableName(getVirtualTableNameIfIsPrimaryKey(
fieldPath, nextSqlType, isPrimaryKey, collectionName, tableNameMap))
.build();
columnMap.put(metadataColumn.getSqlName(), metadataColumn);
addToForeignKeysIfIsPrimary(foreignKeys, isPrimaryKey, metadataColumn);
}
// Ensure virtual table primary key column data types are consistent.
if (isRootDocument) {
checkVirtualTablePrimaryKeys(tableMap, collectionName, columnMap, columnNameMap);
}
// Add virtual table.
final DocumentDbMetadataTable metadataTable = DocumentDbMetadataTable
.builder()
.sqlName(tableName)
.collectionName(collectionName)
.columns(columnMap)
.build();
if (LOGGER.isDebugEnabled() && !tableMap.containsKey(metadataTable.getSqlName())) {
LOGGER.debug(String.format("Added schema for table %s.", metadataTable.getSqlName()));
}
tableMap.put(metadataTable.getSqlName(), metadataTable);
}