001package gudusoft.gsqlparser.sqlenv;
002
003
004import gudusoft.gsqlparser.TBaseType;
005import gudusoft.gsqlparser.util.SQLUtil;
006
007import java.util.*;
008
009/**
010 * In the most sql-implementation, Catalog is the anonymous of the Database.
011 * So, use this class represents a database.
012 */
013public class TSQLCatalog extends TSQLObject {
014
015    // IdentifierService for consistent key generation (lazy initialization).
016    // volatile: concurrent lazy init may build duplicate (equivalent) instances,
017    // but must never publish a partially constructed one to another thread.
018    private volatile IdentifierService identifierService;
019
020    // ===== Phase 6: Catalog seal mechanism for thread-safety hardening =====
021    /**
022     * Seal state flag (volatile for thread visibility).
023     */
024    private volatile boolean sealed = false;
025
026    /**
027     * Seal this catalog to prevent further modifications (Phase 6).
028     *
029     * <p>See {@link TSQLEnv#seal()} for detailed documentation.
030     */
031    public void seal() {
032        this.sealed = true;
033    }
034
035    /**
036     * Check if this catalog is sealed (Phase 6).
037     *
038     * @return true if sealed, false otherwise
039     */
040    public boolean isSealed() {
041        return sealed;
042    }
043
044    /**
045     * Check if catalog is not sealed, throw exception if sealed and enforcement is enabled.
046     *
047     * @throws IllegalStateException if sealed and TBaseType.ENFORCE_CATALOG_SEAL is true
048     */
049    private void checkNotSealed() {
050        if (sealed && TBaseType.ENFORCE_CATALOG_SEAL) {
051            throw new IllegalStateException("Cannot modify sealed catalog. Call seal() marks catalog as read-only.");
052        }
053    }
054
055    /**
056     * The identifier service of the environment this object belongs to (lazy initialization).
057     *
058     * <p>Inherited, never rebuilt from the vendor: this object's local indexes are part of the
059     * same catalog the environment's {@link CatalogStore} indexes, so keying them under the
060     * vendor default while the environment ran a per-category configuration would let one half
061     * of a lookup answer a question the other half never asked.
062     */
063    private IdentifierService getIdentifierService() {
064        if (identifierService == null) {
065            if (this.sqlEnv == null) {
066                // Every catalog object is created against an environment; with none there is no
067                // vendor and no configuration to inherit, so say so instead of guessing one.
068                throw new IllegalStateException(getClass().getSimpleName()
069                        + " has no TSQLEnv, so it has no identifier configuration to compare with");
070            }
071            identifierService = this.sqlEnv.getIdentifierService();
072        }
073        return identifierService;
074    }
075
076    /**
077     * create a catalog and add to the SQL environment.
078     *
079     * @param sqlEnv SQL environment
080     * @param catalogName catalog name
081     */
082    public TSQLCatalog(TSQLEnv sqlEnv, String catalogName){
083        super(sqlEnv,catalogName,ESQLDataObjectType.dotCatalog);
084        this.sqlEnv.doAddCatalog(this);
085    }
086
087    /**
088     * schema list in this catalog
089     *
090     * @return schema list
091     */
092    public List<TSQLSchema> getSchemaList() {
093        List<TSQLSchema> schemaList = new LinkedList<>();
094        synchronized (schemaMap) {
095            for(CanonKey schema: schemaMap.keySet()){
096                schemaList.add(schemaMap.get(schema));
097            }
098        }
099        return schemaList;
100    }
101
102    private Map<CanonKey, TSQLSchema> schemaMap = Collections.synchronizedMap(new LinkedHashMap<CanonKey, TSQLSchema>());
103
104    protected TSQLSchema searchSchema(String schemaName){
105        return schemaMap.get(sqlEnv.getIdentifierService().canonKey(schemaName, ESQLDataObjectType.dotSchema));
106    }
107
108    protected void addSchema(TSQLSchema schema) {
109        synchronized (schemaMap) {
110            checkNotSealed();
111            CanonKey key = sqlEnv.getIdentifierService().canonKeyOf(schema.getName(), ESQLDataObjectType.dotSchema,
112                    IdentifierInputForm.CATALOG_STORED);
113            if (!schemaMap.containsKey(key)) schemaMap.put(key, schema);
114        }
115    }
116
117    /**
118     * get a schema instance from the catalog, create a new schema if this schema is not exist and the createIfNotExist parameter is true.
119     *
120     * @param schemaName schema name
121     * @param createIfNotExist create a new schema if this schema is not exist and the createIfNotExist parameter is true.
122     * @return the schema instance
123     */
124    public TSQLSchema getSchema(String schemaName, boolean createIfNotExist){
125        TSQLSchema result = searchSchema(schemaName);
126        if ((result == null)&&(createIfNotExist)){
127            // synchronized so concurrent get-or-create of the same schema can't
128            // produce duplicate twins; the TSQLSchema constructor re-enters this
129            // monitor via addSchema (reentrant)
130            synchronized (schemaMap) {
131                result = searchSchema(schemaName);
132                if (result == null){
133                    result = new TSQLSchema(this,schemaName);
134                }
135            }
136        }
137
138        return result;
139    }
140
141    /**
142     * create a schema under this catalog
143     *
144     * @param schemaName schema name
145     * @return schema instance
146     */
147    public TSQLSchema createSchema(String schemaName){
148        return getSchema(schemaName,true);
149    }
150
151}