// dispatch parameter type and add it to parameter buffer mPqPB private void AddParameterValue(PqsqlTypeRegistry.PqsqlTypeParameter tp, PqsqlDbType oid, object v) { #if CODECONTRACTS Contract.Assume(mPqPB != IntPtr.Zero); #endif if (v == null || v == DBNull.Value) { // null arrays must have oid of element type PqsqlBinaryFormat.pqbf_add_null(mPqPB, (uint)(oid & ~PqsqlDbType.Array)); } else if ((oid & PqsqlDbType.Array) == PqsqlDbType.Array) { SetArrayValue(mPqPB, v, oid, tp); } else { #if CODECONTRACTS Contract.Assume(tp.SetValue != null); #endif tp.SetValue(mPqPB, v, oid); } }
private static void SetArrayValue(IntPtr pb, object val, PqsqlDbType oid, PqsqlTypeRegistry.PqsqlTypeParameter n) { #if CODECONTRACTS Contract.Requires <ArgumentNullException>(n != null); #else if (n == null) { throw new ArgumentNullException(nameof(n)); } #endif oid &= ~PqsqlDbType.Array; // remove Array flag PqsqlDbType arrayoid = n.ArrayDbType; Action <IntPtr, object> setArrayItem = n.SetArrayItem; Array aparam = val as Array; int rank = aparam.Rank; // TODO we only support one-dimensional array for now if (rank != 1) { throw new NotImplementedException("only one-dimensional arrays supported"); } int[] dim = new int[rank]; int[] lbound = new int[rank]; // always set 1-based numbering for indexes, we cannot reuse lower and upper bounds from aparam for (int i = 0; i < rank; i++) { lbound[i] = 1; dim[i] = aparam.GetLength(i); } IntPtr a = IntPtr.Zero; try { a = UnsafeNativeMethods.PqsqlWrapper.createPQExpBuffer(); if (a == IntPtr.Zero) { throw new PqsqlException("Cannot create buffer for array parameter"); } // check for null values int hasNulls = 0; foreach (object o in aparam) { if (o == null || o == DBNull.Value) { hasNulls = 1; break; } } // create array header PqsqlBinaryFormat.pqbf_set_array(a, rank, hasNulls, (uint)oid, dim, lbound); // copy array items to buffer foreach (object o in aparam) { if (o == null || o == DBNull.Value) // null values have itemlength -1 only { PqsqlBinaryFormat.pqbf_set_array_itemlength(a, -1); } else { setArrayItem(a, o); } } // add array to parameter buffer PqsqlBinaryFormat.pqbf_add_array(pb, a, (uint)arrayoid); } finally { if (a != IntPtr.Zero) { UnsafeNativeMethods.PqsqlWrapper.destroyPQExpBuffer(a); } } }
/// <summary> /// append parameter to parameter buffer. /// we convert and infer the right datatype in case the user supplied inconsistent type information. /// </summary> public void AddParameter(PqsqlParameter parameter) { if (parameter == null) { throw new ArgumentNullException(nameof(parameter)); } ParameterDirection direction = parameter.Direction; // skip output parameters and return values if (direction == ParameterDirection.Output || direction == ParameterDirection.ReturnValue) { return; } PqsqlDbType oid = parameter.PqsqlDbType; object v = parameter.Value; bool vNotNull = v != null && v != DBNull.Value; TypeCode vtc = Convert.GetTypeCode(v); // no PqsqlDbType set by the user, try to infer datatype from Value and set new oid // if v is null or DBNull.Value, we can work with PqsqlDbType.Unknown if (oid == PqsqlDbType.Unknown && vNotNull) { if (vtc != TypeCode.Object) { oid = InferValueType(vtc); } else if (v is DateTimeOffset) { oid = PqsqlDbType.TimestampTZ; } else if (v is byte[]) { oid = PqsqlDbType.Bytea; } else if (v is Guid) { oid = PqsqlDbType.Uuid; } else if (v is TimeSpan) { oid = PqsqlDbType.Interval; } if (oid == PqsqlDbType.Unknown) // cannot resolve oid for non-null v { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not infer datatype for PqsqlParameter {0} (TypeCode={1})", parameter.ParameterName, vtc)); } } // get SetValue / SetArrayItem delegates for oid PqsqlTypeRegistry.PqsqlTypeParameter tp = PqsqlTypeRegistry.Get(oid & ~PqsqlDbType.Array); if (tp == null) { // do not try to fetch datatype specs with PqsqlTypeRegistry.FetchType() here, just bail out throw new NotSupportedException(string.Format(CultureInfo.InvariantCulture, "Datatype {0} is not supported", oid & ~PqsqlDbType.Array)); } // try to convert to the proper datatype in case the user supplied a wrong PqsqlDbType // if v is null or DBNull.Value, we can work with PqsqlDbType.Unknown if (vNotNull && (oid & PqsqlDbType.Array) != PqsqlDbType.Array) { TypeCode tc = tp.TypeCode; if (vtc != TypeCode.Empty && vtc != tc) { v = ConvertParameterValue(v, vtc, tc, oid); } } // add parameter to the parameter buffer AddParameterValue(tp, oid, v); }