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; } }
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); } }
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; } }
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; } }
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); }
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); }
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); }