Beispiel #1
0
        void RegisterBaseType(BackendType backendType)
        {
            TypeAndMapping typeAndMapping;

            if (!HandlerTypes.TryGetValue(backendType.Name, out typeAndMapping))
            {
                // Backend type not supported by Npgsql
                return;
            }

            var handlerType = typeAndMapping.HandlerType;
            var mapping     = typeAndMapping.Mapping;

            // Instantiate the type handler. If it has a constructor that accepts an NpgsqlConnector, use that to allow
            // the handler to make connector-specific adjustments. Otherwise (the normal case), use the default constructor.
            var handler = (TypeHandler)(
                handlerType.GetConstructor(new[] { typeof(TypeHandlerRegistry) }) != null
                    ? Activator.CreateInstance(handlerType, this)
                    : Activator.CreateInstance(handlerType)
                );

            handler.OID = backendType.OID;
            _oidIndex[backendType.OID] = handler;
            handler.PgName             = backendType.Name;

            if (mapping.NpgsqlDbType.HasValue)
            {
                var npgsqlDbType = mapping.NpgsqlDbType.Value;
                if (_byNpgsqlDbType.ContainsKey(npgsqlDbType))
                {
                    throw new Exception(String.Format("Two type handlers registered on same NpgsqlDbType {0}: {1} and {2}",
                                                      npgsqlDbType, _byNpgsqlDbType[npgsqlDbType].GetType().Name, handlerType.Name));
                }
                _byNpgsqlDbType[npgsqlDbType] = handler;
                handler.NpgsqlDbType          = npgsqlDbType;
            }

            foreach (var dbType in mapping.DbTypes)
            {
                if (_byDbType.ContainsKey(dbType))
                {
                    throw new Exception(String.Format("Two type handlers registered on same DbType {0}: {1} and {2}",
                                                      dbType, _byDbType[dbType].GetType().Name, handlerType.Name));
                }
                _byDbType[dbType] = handler;
            }

            foreach (var type in mapping.Types)
            {
                if (_byType.ContainsKey(type))
                {
                    throw new Exception(String.Format("Two type handlers registered on same .NET type {0}: {1} and {2}",
                                                      type, _byType[type].GetType().Name, handlerType.Name));
                }
                _byType[type] = handler;
            }
        }
Beispiel #2
0
        void ActivateEnumType(TypeHandler handler, BackendType backendType)
        {
            handler.PgName                  = backendType.Name;
            handler.OID                     = backendType.OID;
            handler.NpgsqlDbType            = NpgsqlDbType.Enum;
            _oidIndex[backendType.OID]      = handler;
            _byType[handler.GetFieldType()] = handler;

            if (backendType.Array != null)
            {
                RegisterArrayType(backendType.Array);
            }
        }
Beispiel #3
0
        void RegisterArrayType(BackendType backendType)
        {
            Contract.Requires(backendType.Element != null);

            TypeHandler elementHandler;

            if (!_oidIndex.TryGetValue(backendType.Element.OID, out elementHandler))
            {
                // Array type referring to an unhandled element type
                return;
            }

            ArrayHandler arrayHandler;

            var asBitStringHandler = elementHandler as BitStringHandler;

            if (asBitStringHandler != null)
            {
                // BitString requires a special array handler which returns bool or BitArray
                arrayHandler = new BitStringArrayHandler(asBitStringHandler);
            }
            else if (elementHandler is ITypeHandlerWithPsv)
            {
                var arrayHandlerType = typeof(ArrayHandlerWithPsv <,>).MakeGenericType(elementHandler.GetFieldType(), elementHandler.GetProviderSpecificFieldType());
                arrayHandler = (ArrayHandler)Activator.CreateInstance(arrayHandlerType, elementHandler);
            }
            else
            {
                var arrayHandlerType = typeof(ArrayHandler <>).MakeGenericType(elementHandler.GetFieldType());
                arrayHandler = (ArrayHandler)Activator.CreateInstance(arrayHandlerType, elementHandler);
            }

            arrayHandler.PgName        = "array";
            arrayHandler.OID           = backendType.OID;
            _oidIndex[backendType.OID] = arrayHandler;

            if (elementHandler is IEnumHandler)
            {
                if (_byEnumTypeAsArray == null)
                {
                    _byEnumTypeAsArray = new Dictionary <Type, TypeHandler>();
                }
                var enumType = elementHandler.GetType().GetGenericArguments()[0];
                Contract.Assert(enumType.GetTypeInfo().IsEnum);
                _byEnumTypeAsArray[enumType] = arrayHandler;
            }
            else
            {
                _byNpgsqlDbType[NpgsqlDbType.Array | elementHandler.NpgsqlDbType] = arrayHandler;
            }
        }
Beispiel #4
0
        void ActivateEnumType(TypeHandler handler, BackendType backendType)
        {
            handler.PgName                  = backendType.Name;
            handler.OID                     = backendType.OID;
            handler.NpgsqlDbType            = NpgsqlDbType.Enum;
            _oidIndex[backendType.OID]      = handler;
            _byType[handler.GetFieldType()] = handler;

            if (backendType.ArrayOID != 0)
            {
                var arrayHandler = RegisterArrayType(backendType.ArrayOID, handler, backendType.ArrayTextDelimiter);
                if (_byEnumTypeAsArray == null)
                {
                    _byEnumTypeAsArray = new Dictionary <Type, TypeHandler>();
                }
                var enumType = handler.GetType().GetGenericArguments()[0];
                Contract.Assert(enumType.IsEnum);
                _byEnumTypeAsArray[enumType] = arrayHandler;
            }
        }
Beispiel #5
0
        void RegisterRangeType(BackendType backendType)
        {
            Contract.Requires(backendType.Element != null);

            TypeHandler elementHandler;

            if (!_oidIndex.TryGetValue(backendType.Element.OID, out elementHandler))
            {
                // Range type referring to an unhandled element type
                return;
            }

            var rangeHandlerType = typeof(RangeHandler <>).MakeGenericType(elementHandler.GetFieldType());
            var handler          = (TypeHandler)Activator.CreateInstance(rangeHandlerType, elementHandler, backendType.Name);

            handler.PgName             = backendType.Name;
            handler.NpgsqlDbType       = NpgsqlDbType.Range | elementHandler.NpgsqlDbType;
            handler.OID                = backendType.OID;
            _oidIndex[backendType.OID] = handler;
            _byNpgsqlDbType.Add(handler.NpgsqlDbType, handler);
        }
Beispiel #6
0
        static List <BackendType> LoadBackendTypes(NpgsqlConnector connector)
        {
            var byOID = new Dictionary <uint, BackendType>();

            // Select all types (base, array which is also base, enum, range). Note that arrays are distinguished
            // from primitive types through them having typelem != 0 and typlen == -1.
            // Order by primitives first, container later.
            // For arrays and ranges, join in the element OID and type (to filter out arrays of unhandled
            // types).
            var query =
                @"SELECT a.typname, a.oid, a.typtype, a.typlen, CASE " +
                (connector.SupportsRangeTypes ? @"WHEN a.typtype='r' THEN rngsubtype " : "") +
                @"WHEN a.typlen = -1 THEN a.typelem ELSE 0 END AS ord " +
                @"FROM pg_type AS a " +
                @"LEFT OUTER JOIN pg_type AS b ON (b.oid = a.typelem) " +
                (connector.SupportsRangeTypes ? @"LEFT OUTER JOIN pg_range ON (pg_range.rngtypid = a.oid) " : "") +
                @"WHERE a.typtype IN ('b', 'r', 'e') AND (b.typtype IS NULL OR b.typtype IN ('b', 'r', 'e'))" +
                @"ORDER BY ord";

            var types = new List <BackendType>();

            using (var command = new NpgsqlCommand(query, connector))
            {
                command.AllResultTypesAreUnknown = true;
                using (var dr = command.ExecuteReader(CommandBehavior.SequentialAccess))
                {
                    while (dr.Read())
                    {
                        var backendType = new BackendType
                        {
                            Name = dr.GetString(0),
                            OID  = Convert.ToUInt32(dr[1])
                        };

                        Contract.Assume(backendType.Name != null);
                        Contract.Assume(backendType.OID != 0);

                        uint elementOID;
                        var  typeChar = dr.GetString(2)[0];
                        switch (typeChar)
                        {
                        case 'b':
                            // We can't use pg_type.typarray to identify array types because that appeared only in PG 8.3.
                            // But "True" arrays have typelem != 0 and typlen == -1
                            var typeLen = Convert.ToInt16(dr[3]);
                            elementOID = Convert.ToUInt32(dr[4]);
                            if (elementOID != 0 && typeLen == -1)
                            {
                                backendType.Type = BackendTypeType.Array;
                                if (!byOID.TryGetValue(elementOID, out backendType.Element))
                                {
                                    Log.ErrorFormat("Array type '{0}' refers to unknown element with OID {1}, skipping", backendType.Name, elementOID);
                                    continue;
                                }
                                backendType.Element.Array = backendType;
                            }
                            else
                            {
                                backendType.Type = BackendTypeType.Base;
                            }
                            break;

                        case 'e':
                            backendType.Type = BackendTypeType.Enum;
                            break;

                        case 'r':
                            backendType.Type = BackendTypeType.Range;
                            elementOID       = Convert.ToUInt32(dr[4]);
                            if (!byOID.TryGetValue(elementOID, out backendType.Element))
                            {
                                Log.ErrorFormat("Range type '{0}' refers to unknown subtype with OID {1}, skipping", backendType.Name, elementOID);
                                continue;
                            }
                            break;

                        default:
                            throw new ArgumentOutOfRangeException(String.Format("Unknown typtype for type '{0}' in pg_type: {1}", backendType.Name, typeChar));
                        }

                        types.Add(backendType);
                        byOID[backendType.OID] = backendType;
                    }
                }
            }

            /*foreach (var notFound in _typeHandlers.Where(t => t.Oid == -1)) {
             *  _log.WarnFormat("Could not find type {0} in pg_type", notFound.PgNames[0]);
             * }*/

            return(types);
        }
Beispiel #7
0
        static List <BackendType> LoadBackendTypes(NpgsqlConnector connector)
        {
            var byOID = new Dictionary <uint, BackendType>();

            // Select all types (base, array which is also base, enum, range).
            // Note that arrays are distinguished from primitive types through them having typreceive=array_recv.
            // Order by primitives first, container later.
            // For arrays and ranges, join in the element OID and type (to filter out arrays of unhandled
            // types).
            var query =
                @"SELECT a.typname, a.oid, " +
                @"CASE WHEN pg_proc.proname='array_recv' THEN 'a' ELSE a.typtype END AS type, " +
                @"CASE " +
                @"WHEN pg_proc.proname='array_recv' THEN a.typelem " +
                (connector.SupportsRangeTypes ? @"WHEN a.typtype='r' THEN rngsubtype " : "") +
                @"ELSE 0 " +
                @"END AS elemoid, " +
                @"CASE " +
                @"WHEN pg_proc.proname IN ('array_recv','oidvectorrecv') THEN 2 " +    // Arrays last
                @"WHEN a.typtype='r' THEN 1 " +                                        // Ranges before
                @"ELSE 0 " +                                                           // Base types first
                @"END AS ord " +
                @"FROM pg_type AS a " +
                @"JOIN pg_proc ON pg_proc.oid = a.typreceive " +
                @"LEFT OUTER JOIN pg_type AS b ON (b.oid = a.typelem) " +
                (connector.SupportsRangeTypes ? @"LEFT OUTER JOIN pg_range ON (pg_range.rngtypid = a.oid) " : "") +
                @"WHERE a.typtype IN ('b', 'r', 'e') AND (b.typtype IS NULL OR b.typtype IN ('b', 'r', 'e')) " +
                @"ORDER BY ord";

            var types = new List <BackendType>();

            using (var command = new NpgsqlCommand(query, connector.Connection))
            {
                command.AllResultTypesAreUnknown = true;
                using (var dr = command.ExecuteReader(CommandBehavior.SequentialAccess))
                {
                    while (dr.Read())
                    {
                        var backendType = new BackendType
                        {
                            Name = dr.GetString(0),
                            OID  = Convert.ToUInt32(dr[1])
                        };

                        Contract.Assume(backendType.Name != null);
                        Contract.Assume(backendType.OID != 0);

                        uint elementOID;
                        var  typeChar = dr.GetString(2)[0];
                        switch (typeChar)
                        {
                        case 'b':  // Normal base type
                            backendType.Type = BackendTypeType.Base;
                            break;

                        case 'a':   // Array
                            backendType.Type = BackendTypeType.Array;
                            elementOID       = Convert.ToUInt32(dr[3]);
                            Contract.Assume(elementOID > 0);
                            if (!byOID.TryGetValue(elementOID, out backendType.Element))
                            {
                                Log.Trace(string.Format("Array type '{0}' refers to unknown element with OID {1}, skipping", backendType.Name, elementOID), connector.Id);
                                continue;
                            }
                            backendType.Element.Array = backendType;
                            break;

                        case 'e':   // Enum
                            backendType.Type = BackendTypeType.Enum;
                            break;

                        case 'r':   // Range
                            backendType.Type = BackendTypeType.Range;
                            elementOID       = Convert.ToUInt32(dr[3]);
                            Contract.Assume(elementOID > 0);
                            if (!byOID.TryGetValue(elementOID, out backendType.Element))
                            {
                                Log.Error(String.Format("Range type '{0}' refers to unknown subtype with OID {1}, skipping", backendType.Name, elementOID), connector.Id);
                                continue;
                            }
                            break;

                        default:
                            throw new ArgumentOutOfRangeException(String.Format("Unknown typtype for type '{0}' in pg_type: {1}", backendType.Name, typeChar));
                        }

                        types.Add(backendType);
                        byOID[backendType.OID] = backendType;
                    }
                }
            }

            /*foreach (var notFound in _typeHandlers.Where(t => t.Oid == -1)) {
             *  _log.WarnFormat("Could not find type {0} in pg_type", notFound.PgNames[0]);
             * }*/

            return(types);
        }