Package com.impossibl.postgres.types

Source Code of com.impossibl.postgres.types.Registry

/**
* Copyright (c) 2013, impossibl.com
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*  * Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
*  * Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
*  * Neither the name of impossibl.com nor the names of its contributors may
*    be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package com.impossibl.postgres.types;

import com.impossibl.postgres.protocol.ResultField.Format;
import com.impossibl.postgres.system.Context;
import com.impossibl.postgres.system.procs.Procs;
import com.impossibl.postgres.system.tables.PgAttribute;
import com.impossibl.postgres.system.tables.PgProc;
import com.impossibl.postgres.system.tables.PgType;
import com.impossibl.postgres.types.Type.Category;
import com.impossibl.postgres.types.Type.Codec;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;

/**
* Storage and loading for all the known types of a given context.
*
* @author kdubb
*
*/
public class Registry {

  private static Logger logger = Logger.getLogger(Registry.class.getName());

  private Map<Character, Class<? extends Type>> kindMap;
  private TreeMap<Integer, Type> oidMap;
  private Map<String, Type> nameMap;
  private TreeMap<Integer, Type> relIdMap;

  private TreeMap<Integer, PgType.Row> pgTypeData;
  private TreeMap<Integer, Collection<PgAttribute.Row>> pgAttrData;
  private TreeMap<Integer, PgProc.Row> pgProcData;
  private Map<String, PgProc.Row> pgProcNameMap;

  private Context context;
  private ReadWriteLock lock = new ReentrantReadWriteLock();

  private Map<String, String> typeNameAliases;

  public Registry(Context context) {


    this.context = context;

    pgTypeData = new TreeMap<>();
    pgAttrData = new TreeMap<>();
    pgProcData = new TreeMap<>();
    pgProcNameMap = new HashMap<>();

    //Maps kinds to their associated type class
    kindMap = new HashMap<>();
    kindMap.put('c', CompositeType.class);
    kindMap.put('d', DomainType.class);
    kindMap.put('e', EnumerationType.class);
    kindMap.put('p', PsuedoType.class);
    kindMap.put('r', RangeType.class);

    // Required initial types for bootstrapping
    oidMap = new TreeMap<Integer, Type>();
    oidMap.put(16, new BaseType(16, "bool",     (short) 1(byte) 0, Category.Boolean, ',', 0, "bool"));
    oidMap.put(17, new BaseType(17, "bytea",    (short) 1(byte) 0, Category.User,    ',', 0, "bytea"));
    oidMap.put(18, new BaseType(18, "char",     (short) 1(byte) 0, Category.String,  ',', 0, "char"));
    oidMap.put(19, new BaseType(19, "name",     (short) 64, (byte) 0, Category.String,  ',', 0, "name"));
    oidMap.put(21, new BaseType(21, "int2",     (short) 2(byte) 0, Category.Numeric, ',', 0, "int2"));
    oidMap.put(23, new BaseType(23, "int4",     (short) 4(byte) 0, Category.Numeric, ',', 0, "int4"));
    oidMap.put(24, new BaseType(24, "regproc"(short) 4(byte) 0, Category.Numeric, ',', 0, "regproc"));
    oidMap.put(25, new BaseType(25, "text",     (short) 1(byte) 0, Category.String,  ',', 0, "text"));
    oidMap.put(26, new BaseType(26, "oid",      (short) 4(byte) 0, Category.Numeric, ',', 0, "oid"));

    relIdMap = new TreeMap<>();
    nameMap = new HashMap<>();

    typeNameAliases = new HashMap<>();
    typeNameAliases.put("smallint", "int2");
    typeNameAliases.put("integer", "int4");
    typeNameAliases.put("bigint", "int8");
    typeNameAliases.put("decimal", "numeric");
    typeNameAliases.put("real", "float4");
    typeNameAliases.put("double precision", "float8");
    typeNameAliases.put("smallserial", "int2");
    typeNameAliases.put("serial", "int4");
    typeNameAliases.put("bigserial", "int8");
  }

  /**
   * Loads a type by its type-id (aka OID)
   *
   * @param typeId The type's id
   * @return Type object or null, if none found
   */
  public Type loadType(int typeId) {

    if (typeId == 0)
      return null;

    lock.readLock().lock();
    try {

      Type type = oidMap.get(typeId);
      if (type == null) {

        lock.readLock().unlock();
        try {

          type = loadRaw(typeId);

          if (type == null) {

            context.refreshType(typeId);

          }

        }
        finally {
          lock.readLock().lock();
        }

        type = oidMap.get(typeId);

      }

      return type;

    }
    finally {
      lock.readLock().unlock();
    }

  }

  /**
   * Loads a type by its name
   *
   * @param name The type's name
   * @return Type object or null, if none found
   */
  public Type loadType(String name) {

    boolean isArray = false;
    if (name .endsWith("[]")) {
      isArray = true;
      name = name.substring(0, name.length() - 2);
    }


    String alias = typeNameAliases.get(name);
    if (alias != null) {
      name = alias;
    }


    Type res;

    lock.readLock().lock();
    try {
      res = nameMap.get(name);
    }
    finally {
      lock.readLock().unlock();
    }

    if (res == null) {
      context.refreshType(getLatestKnownTypeId() + 1);
      lock.readLock().lock();
      try {
        res = nameMap.get(name);
      }
      finally {
        lock.readLock().unlock();
      }
    }

    if (isArray && res != null) {
      res = loadType(res.getArrayTypeId());
    }

    return res;
  }

  /**
   * Loads a relation (aka table) type by its relation-id
   *
   * @param relationId Relation ID of the type to load
   * @return Relation type or null
   */
  public CompositeType loadRelationType(int relationId) {

    if (relationId == 0)
      return null;

    lock.readLock().lock();
    try {

      CompositeType type = (CompositeType) relIdMap.get(relationId);
      if (type == null) {

        lock.readLock().unlock();
        try {

          type = loadRelationRaw(relationId);

          if (type == null) {

            context.refreshRelationType(relationId);

          }

        }
        finally {
          lock.readLock().lock();
        }

        type = (CompositeType) relIdMap.get(relationId);

      }

      return type;
    }
    finally {
      lock.readLock().unlock();
    }

  }

  public void unloadType(int oid) {
    Type type = oidMap.remove(oid);
    if (type != null) {
      nameMap.remove(type.getName());
    }
  }

  public void unloadType(String name) {
    Type type = nameMap.remove(name);
    if (type != null) {
      oidMap.remove(type.getId());
    }
  }

  /**
   * Looks up a procedures name given it's proc-id (aka OID)
   *
   * @param procId The procedure's id
   * @return The text name of the procedure or null, if none found
   */
  public String lookupProcName(int procId) {

    lock.readLock().lock();
    try {

      PgProc.Row pgProc = pgProcData.get(procId);
      if (pgProc == null)
        return null;

      return pgProc.name;
    }
    finally {
      lock.readLock().unlock();
    }
  }

  /**
   * Looks up a procedure id (aka OID) given it's name.
   *
   * @param procName The procedure's name
   * @return The id of the procedure (aka OID) or null, if none found
   */
  public int lookupProcId(String procName) {

    lock.readLock().lock();
    try {

      PgProc.Row pgProc = pgProcNameMap.get(procName);
      if (pgProc == null)
        return 0;

      return pgProc.oid;
    }
    finally {
      lock.readLock().unlock();
    }

  }

  public int getLatestKnownTypeId() {
    return oidMap.lastKey();
  }

  /**
   * Updates the type information from the given catalog data. Any
   * information not specifically mentioned in the given updated
   * data will be retained and untouched.
   *
   * @param pgTypeRows "pg_type" table rows
   * @param pgAttrRows "pg_attribute" table rows
   * @param pgProcRows "pg_proc" table rows
   */
  public void update(Collection<PgType.Row> pgTypeRows, Collection<PgAttribute.Row> pgAttrRows, Collection<PgProc.Row> pgProcRows) {

    lock.writeLock().lock();
    try {
      /*
       * Update attribute info
       */

      //Remove attribute info for updating types
      for (PgType.Row pgType : pgTypeRows) {
        pgAttrData.remove(pgType.relationId);
      }

      //Add updated info
      for (PgAttribute.Row pgAttrRow : pgAttrRows) {

        Collection<PgAttribute.Row> relRows = pgAttrData.get(pgAttrRow.relationId);
        if (relRows == null) {
          relRows = new HashSet<PgAttribute.Row>();
          pgAttrData.put(pgAttrRow.relationId, relRows);
        }

        relRows.add(pgAttrRow);
      }

      /*
       * Update proc info
       */

      for (PgProc.Row pgProcRow : pgProcRows) {
        pgProcData.put(pgProcRow.oid, pgProcRow);
        pgProcNameMap.put(pgProcRow.name, pgProcRow);
      }


      /*
       * Update type info
       */
      for (PgType.Row pgTypeRow : pgTypeRows) {
        pgTypeData.put(pgTypeRow.oid, pgTypeRow);
        oidMap.remove(pgTypeRow.oid);
        nameMap.remove(pgTypeRow.name);
        relIdMap.remove(pgTypeRow.relationId);
      }

    }
    finally {
      lock.writeLock().unlock();
    }

    /*
     * (re)load all types just updated
     */
    for (PgType.Row pgType : pgTypeRows) {
      loadType(pgType.oid);
    }

  }

  /*
   * Materialize the requested type from the raw catalog data
   */
  private Type loadRaw(int typeId) {

    if (typeId == 0)
      return null;

    lock.writeLock().lock();
    try {

      PgType.Row pgType = pgTypeData.get(typeId);
      if (pgType == null)
        return null;

      Collection<PgAttribute.Row> pgAttrs = pgAttrData.get(pgType.relationId);

      Type type;

      lock.writeLock().unlock();
      try {
        type = loadRaw(pgType, pgAttrs);
      }
      finally {
        lock.writeLock().lock();
      }

      if (type != null) {
        oidMap.put(typeId, type);
        nameMap.put(type.getName(), type);
        relIdMap.put(type.getRelationId(), type);
      }

      return type;
    }
    finally {
      lock.writeLock().unlock();
    }
  }

  /*
   * Materialize the requested type from the raw catalog data
   */
  private CompositeType loadRelationRaw(int relationId) {

    if (relationId == 0)
      return null;

    lock.writeLock().lock();
    try {

      CompositeType type = null;

      Collection<PgAttribute.Row> pgAttrs = pgAttrData.get(relationId);
      if (pgAttrs != null && !pgAttrs.isEmpty()) {

        PgType.Row pgType = pgTypeData.get(pgAttrs.iterator().next().relationTypeId);
        if (pgType == null)
          return null;

        lock.writeLock().unlock();
        try {
          type = (CompositeType) loadRaw(pgType, pgAttrs);
        }
        finally {
          lock.writeLock().lock();
        }

        if (type != null) {
          oidMap.put(pgType.oid, type);
          nameMap.put(type.getName(), type);
          relIdMap.put(type.getRelationId(), type);
        }
      }

      return type;
    }
    finally {
      lock.writeLock().unlock();
    }

  }

  /*
   * Materialize a type from the given "pg_type" and "pg_attribute" data
   */
  private Type loadRaw(PgType.Row pgType, Collection<PgAttribute.Row> pgAttrs) {

    Type type;

    if (pgType.elementTypeId != 0 && pgType.category.equals("A")) {

      ArrayType array = new ArrayType();
      array.setElementType(loadType(pgType.elementTypeId));

      type = array;
    }
    else {

      switch(pgType.discriminator.charAt(0)) {
        case 'b':
          type = new BaseType();
          break;
        case 'c':
          type = new CompositeType();
          break;
        case 'd':
          type = new DomainType();
          break;
        case 'e':
          type = new EnumerationType();
          break;
        case 'p':
          type = new PsuedoType();
          break;
        case 'r':
          type = new RangeType();
          break;
        default:
          logger.warning("unknown discriminator (aka 'typtype') found in pg_type table");
          return null;
      }

    }

    try {

      lock.writeLock().lock();
      try {
        oidMap.put(pgType.oid, type);
      }
      finally {
        lock.writeLock().unlock();
      }

      type.load(pgType, pgAttrs, this);

    }
    catch (Exception e) {

      e.printStackTrace();

      lock.writeLock().lock();
      try {
        oidMap.remove(pgType.oid);
      }
      finally {
        lock.writeLock().unlock();
      }
    }

    return type;
  }

  /**
   * Loads a matching Codec given the proc-id of its encoder and decoder
   *
   * @param encoderId proc-id of the encoder
   * @param decoderId proc-id of the decoder
   * @return A matching Codec instance
   */
  public Codec loadCodec(int encoderId, int decoderId, Format format) {

    Codec io = new Codec();
    io.decoder = loadDecoderProc(decoderId, Procs.getDefaultDecoder(format));
    io.encoder = loadEncoderProc(encoderId, Procs.getDefaultEncoder(format));
    return io;
  }

  /*
   * Loads a matching encoder given its proc-id
   */
  private Codec.Encoder loadEncoderProc(int procId, Codec.Encoder defaultEncoder) {

    String name = lookupProcName(procId);
    if (name == null) {
      return defaultEncoder;
    }

    return Procs.loadEncoderProc(name, context, defaultEncoder);
  }

  /*
   * Loads a matching decoder given its proc-id
   */
  private Codec.Decoder loadDecoderProc(int procId, Codec.Decoder defaultDecoder) {

    String name = lookupProcName(procId);
    if (name == null) {
      return defaultDecoder;
    }

    return Procs.loadDecoderProc(name, context, defaultDecoder);
  }

  /*
   * Loads a matching parser given mod-in and mod-out ids
   */
  public Modifiers.Parser loadModifierParser(int modInId, int modOutId) {

    String name = lookupProcName(modInId);
    if (name == null) {
      name = ""; //get the default proc
    }

    return Procs.loadModifierParserProc(name, context);
  }

}
TOP

Related Classes of com.impossibl.postgres.types.Registry

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.