private static void processArray()

in calcite-adapter/src/main/java/software/amazon/documentdb/jdbc/metadata/DocumentDbTableSchemaGenerator.java [267:433]


    private static void processArray(
            final BsonArray array,
            final Map<String, DocumentDbSchemaTable> tableMap,
            final List<DocumentDbMetadataColumn> foreignKeys,
            final String path,
            final int arrayLevel,
            final String collectionName,
            final Map<String, String> tableNameMap) {

        // Need to preserve order of fields.
        final LinkedHashMap<String, DocumentDbSchemaColumn> columnMap = new LinkedHashMap<>();

        int primaryKeyColumn = KEY_COLUMN_NONE;
        int level = arrayLevel;
        DocumentDbMetadataColumn metadataColumn;

        JdbcType prevSqlType = JdbcType.NULL;
        JdbcType sqlType;
        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());
            final String valueColumnPath = VALUE_COLUMN_NAME;
            // TODO: Figure out if previous type was array of array.
            if (columnMap.containsKey(toName(valueColumnPath, tableNameMap))) {
                prevSqlType = columnMap.get(toName(valueColumnPath, tableNameMap)).getSqlType();
            } else {
                prevSqlType = JdbcType.JAVA_OBJECT;
            }
        }

        // Find the promoted SQL data type for all elements.
        sqlType = prevSqlType;
        for (BsonValue element : array) {
            sqlType = getPromotedSqlType(element.getBsonType(), sqlType);
            if (LOGGER.isDebugEnabled()) {
                final JdbcType currentSqlType = getPromotedSqlType(element.getBsonType(), JdbcType.NULL);
                if (!prevSqlType.equals(currentSqlType)) {
                    LOGGER.debug(String.format("Type conflict in array table %s, types %s and %s mapped to %s.",
                            tableName, prevSqlType.name(), currentSqlType, sqlType.name()));
                }
            }
        }
        if (!isComplexType(sqlType)) {
            if (isComplexType(prevSqlType)) {
                // If promoted to scalar type from complex type, remove previous definition.
                handleComplexScalarConflict(tableMap, tableName, columnMap);
            } else {
                // Check to see if we're processing scalars at a different level than previously
                // detected.
                sqlType = handleArrayLevelConflict(columnMap, level, sqlType);
            }
        } else if (isComplexType(sqlType) && !isComplexType(prevSqlType)) {
            // Promoted from NULL to ARRAY or OBJECT.
            handleComplexScalarConflict(tableMap, tableName, columnMap);
        }

        if (!tableMap.containsKey(path)) {
            // 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.
            for (DocumentDbMetadataColumn column : foreignKeys) {
                primaryKeyColumn++;
                metadataColumn = DocumentDbMetadataColumn
                        .builder()
                        .fieldPath(column.getFieldPath())
                        .sqlName(column.getSqlName())
                        .sqlType(column.getSqlType())
                        .dbType(column.getDbType())
                        .isIndex(column.isIndex())
                        .isPrimaryKey(primaryKeyColumn != 0)
                        .foreignKeyTableName(column.getTableName().equals(tableName)
                                ? null
                                : column.getTableName())
                        .index(column.getIndex())
                        .tableName(tableName)
                        .primaryKeyIndex(primaryKeyColumn)
                        .foreignKeyIndex(column.getTableName().equals(tableName)
                                ? KEY_COLUMN_NONE
                                : primaryKeyColumn)
                        .virtualTableName(column.getVirtualTableName())
                        .arrayIndexLevel(column.getArrayIndexLevel())
                        .isGenerated(column.isGenerated())
                        .build();
                metadataColumn.setForeignKeyColumnName(metadataColumn.getForeignKeyTableName() != null
                        ? column.getSqlName()
                        : null);
                columnMap.put(metadataColumn.getSqlName(), metadataColumn);
            }

            final Map<String, String> columnNameMap = columnMap.values().stream().collect(
                    Collectors.toMap(
                            DocumentDbSchemaColumn::getSqlName,
                            DocumentDbSchemaColumn::getSqlName));
            final String indexColumnName = toName(
                    combinePath(path, INDEX_COLUMN_NAME_PREFIX + level),
                    columnNameMap);
            final DocumentDbMetadataColumn indexColumn;
            if (!columnMap.containsKey(indexColumnName)) {
                // Add index column. Although it has no path in the original document, we will
                // use the path of the generated index field once the original document is unwound.
                primaryKeyColumn++;
                indexColumn = DocumentDbMetadataColumn
                        .builder()
                        .sqlName(indexColumnName)
                        .fieldPath(path)  // Once unwound, the index will be at root level so path = name.
                        .sqlType(JdbcType.BIGINT)
                        .isIndex(true)
                        .isPrimaryKey(true)
                        .index(columnMap.size() + 1)
                        .tableName(tableName)
                        .primaryKeyIndex(primaryKeyColumn)
                        .foreignKeyIndex(KEY_COLUMN_NONE)
                        .arrayIndexLevel(level)
                        .isGenerated(true)
                        .build();
                columnMap.put(indexColumn.getSqlName(), indexColumn);
            } else {
                // Cast exception should not occur, because we are always creating DocumentDbMetadataColumn.
                indexColumn = (DocumentDbMetadataColumn) columnMap.get(indexColumnName);
            }
            // Add index column to foreign keys
            foreignKeys.add(indexColumn);
        }

        // Add documents, arrays or just the scalar value.
        switch (sqlType) {
            case JAVA_OBJECT:
                processDocumentsInArray(array,
                        tableMap,
                        foreignKeys,
                        path,
                        collectionName,
                        tableNameMap);
                break;
            case ARRAY:
                // This will add another level to the array.
                level++;
                processArrayInArray(array,
                        tableMap,
                        foreignKeys,
                        path,
                        collectionName,
                        level,
                        tableNameMap);
                break;
            default:
                processValuesInArray(
                        tableMap,
                        path,
                        collectionName,
                        columnMap,
                        sqlType,
                        tableNameMap);
                break;
        }
    }