mirror of
https://github.com/postgres/postgres.git
synced 2025-11-10 17:42:29 +03:00
Convert oidvector and int2vector into variable-length arrays. This
change saves a great deal of space in pg_proc and its primary index, and it eliminates the former requirement that INDEX_MAX_KEYS and FUNC_MAX_ARGS have the same value. INDEX_MAX_KEYS is still embedded in the on-disk representation (because it affects index tuple header size), but FUNC_MAX_ARGS is not. I believe it would now be possible to increase FUNC_MAX_ARGS at little cost, but haven't experimented yet. There are still a lot of vestigial references to FUNC_MAX_ARGS, which I will clean up in a separate pass. However, getting rid of it altogether would require changing the FunctionCallInfoData struct, and I'm not sure I want to buy into that.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.117 2005/03/24 21:50:37 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.118 2005/03/29 00:17:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -56,7 +56,7 @@
|
||||
*
|
||||
*
|
||||
* There are also some "fixed-length array" datatypes, such as NAME and
|
||||
* OIDVECTOR. These are simply a sequence of a fixed number of items each
|
||||
* POINT. These are simply a sequence of a fixed number of items each
|
||||
* of a fixed-length datatype, with no overhead; the item size must be
|
||||
* a multiple of its alignment requirement, because we do no padding.
|
||||
* We support subscripting on these types, but array_in() and array_out()
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.39 2004/12/31 22:01:21 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.40 2005/03/29 00:17:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -140,12 +140,15 @@ format_type_internal(Oid type_oid, int32 typemod,
|
||||
/*
|
||||
* Check if it's an array (and not a domain --- we don't want to show
|
||||
* the substructure of a domain type). Fixed-length array types such
|
||||
* as "name" shouldn't get deconstructed either.
|
||||
* as "name" shouldn't get deconstructed either. As of Postgres 8.1,
|
||||
* rather than checking typlen we check the toast property, and don't
|
||||
* deconstruct "plain storage" array types --- this is because we don't
|
||||
* want to show oidvector as oid[].
|
||||
*/
|
||||
array_base_type = typeform->typelem;
|
||||
|
||||
if (array_base_type != InvalidOid &&
|
||||
typeform->typlen == -1 &&
|
||||
typeform->typstorage != 'p' &&
|
||||
typeform->typtype != 'd')
|
||||
{
|
||||
/* Switch our attention to the array element type */
|
||||
@@ -459,29 +462,17 @@ type_maximum_size(Oid type_oid, int32 typemod)
|
||||
|
||||
/*
|
||||
* oidvectortypes - converts a vector of type OIDs to "typname" list
|
||||
*
|
||||
* The interface for this function is wrong: it should be told how many
|
||||
* OIDs are significant in the input vector, so that trailing InvalidOid
|
||||
* argument types can be recognized.
|
||||
*/
|
||||
Datum
|
||||
oidvectortypes(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *oidArray = (Oid *) PG_GETARG_POINTER(0);
|
||||
oidvector *oidArray = (oidvector *) PG_GETARG_POINTER(0);
|
||||
char *result;
|
||||
int numargs;
|
||||
int numargs = oidArray->dim1;
|
||||
int num;
|
||||
size_t total;
|
||||
size_t left;
|
||||
|
||||
/* Try to guess how many args there are :-( */
|
||||
numargs = 0;
|
||||
for (num = 0; num < FUNC_MAX_ARGS; num++)
|
||||
{
|
||||
if (oidArray[num] != InvalidOid)
|
||||
numargs = num + 1;
|
||||
}
|
||||
|
||||
total = 20 * numargs + 1;
|
||||
result = palloc(total);
|
||||
result[0] = '\0';
|
||||
@@ -489,7 +480,7 @@ oidvectortypes(PG_FUNCTION_ARGS)
|
||||
|
||||
for (num = 0; num < numargs; num++)
|
||||
{
|
||||
char *typename = format_type_internal(oidArray[num], -1,
|
||||
char *typename = format_type_internal(oidArray->values[num], -1,
|
||||
false, true);
|
||||
size_t slen = strlen(typename);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.65 2005/02/27 08:31:30 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/int.c,v 1.66 2005/03/29 00:17:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -33,8 +33,10 @@
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "funcapi.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
|
||||
@@ -47,6 +49,8 @@
|
||||
|
||||
#define SAMESIGN(a,b) (((a) < 0) == ((b) < 0))
|
||||
|
||||
#define Int2VectorSize(n) (offsetof(int2vector, values) + (n) * sizeof(int2))
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int32 current;
|
||||
@@ -109,20 +113,49 @@ int2send(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* int2vectorin - converts "num num ..." to internal form
|
||||
* construct int2vector given a raw array of int2s
|
||||
*
|
||||
* Note: Fills any missing slots with zeroes.
|
||||
* If int2s is NULL then caller must fill values[] afterward
|
||||
*/
|
||||
int2vector *
|
||||
buildint2vector(const int2 *int2s, int n)
|
||||
{
|
||||
int2vector *result;
|
||||
|
||||
result = (int2vector *) palloc0(Int2VectorSize(n));
|
||||
|
||||
if (n > 0 && int2s)
|
||||
memcpy(result->values, int2s, n * sizeof(int2));
|
||||
|
||||
/*
|
||||
* Attach standard array header. For historical reasons, we set the
|
||||
* index lower bound to 0 not 1.
|
||||
*/
|
||||
result->size = Int2VectorSize(n);
|
||||
result->ndim = 1;
|
||||
result->flags = 0;
|
||||
result->elemtype = INT2OID;
|
||||
result->dim1 = n;
|
||||
result->lbound1 = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* int2vectorin - converts "num num ..." to internal form
|
||||
*/
|
||||
Datum
|
||||
int2vectorin(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *intString = PG_GETARG_CSTRING(0);
|
||||
int16 *result = (int16 *) palloc(sizeof(int16[INDEX_MAX_KEYS]));
|
||||
int slot;
|
||||
int2vector *result;
|
||||
int n;
|
||||
|
||||
for (slot = 0; *intString && slot < INDEX_MAX_KEYS; slot++)
|
||||
result = (int2vector *) palloc0(Int2VectorSize(FUNC_MAX_ARGS));
|
||||
|
||||
for (n = 0; *intString && n < FUNC_MAX_ARGS; n++)
|
||||
{
|
||||
if (sscanf(intString, "%hd", &result[slot]) != 1)
|
||||
if (sscanf(intString, "%hd", &result->values[n]) != 1)
|
||||
break;
|
||||
while (*intString && isspace((unsigned char) *intString))
|
||||
intString++;
|
||||
@@ -136,8 +169,12 @@ int2vectorin(PG_FUNCTION_ARGS)
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("int2vector has too many elements")));
|
||||
|
||||
while (slot < INDEX_MAX_KEYS)
|
||||
result[slot++] = 0;
|
||||
result->size = Int2VectorSize(n);
|
||||
result->ndim = 1;
|
||||
result->flags = 0;
|
||||
result->elemtype = INT2OID;
|
||||
result->dim1 = n;
|
||||
result->lbound1 = 0;
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
@@ -148,24 +185,19 @@ int2vectorin(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
int2vectorout(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16 *int2Array = (int16 *) PG_GETARG_POINTER(0);
|
||||
int2vector *int2Array = (int2vector *) PG_GETARG_POINTER(0);
|
||||
int num,
|
||||
maxnum;
|
||||
nnums = int2Array->dim1;
|
||||
char *rp;
|
||||
char *result;
|
||||
|
||||
/* find last non-zero value in vector */
|
||||
for (maxnum = INDEX_MAX_KEYS - 1; maxnum >= 0; maxnum--)
|
||||
if (int2Array[maxnum] != 0)
|
||||
break;
|
||||
|
||||
/* assumes sign, 5 digits, ' ' */
|
||||
rp = result = (char *) palloc((maxnum + 1) * 7 + 1);
|
||||
for (num = 0; num <= maxnum; num++)
|
||||
rp = result = (char *) palloc(nnums * 7 + 1);
|
||||
for (num = 0; num < nnums; num++)
|
||||
{
|
||||
if (num != 0)
|
||||
*rp++ = ' ';
|
||||
pg_itoa(int2Array[num], rp);
|
||||
pg_itoa(int2Array->values[num], rp);
|
||||
while (*++rp != '\0')
|
||||
;
|
||||
}
|
||||
@@ -180,11 +212,19 @@ Datum
|
||||
int2vectorrecv(PG_FUNCTION_ARGS)
|
||||
{
|
||||
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
||||
int16 *result = (int16 *) palloc(sizeof(int16[INDEX_MAX_KEYS]));
|
||||
int slot;
|
||||
int2vector *result;
|
||||
|
||||
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
||||
result[slot] = (int16) pq_getmsgint(buf, sizeof(int16));
|
||||
result = (int2vector *)
|
||||
DatumGetPointer(DirectFunctionCall2(array_recv,
|
||||
PointerGetDatum(buf),
|
||||
ObjectIdGetDatum(INT2OID)));
|
||||
/* sanity checks: int2vector must be 1-D, no nulls */
|
||||
if (result->ndim != 1 ||
|
||||
result->flags != 0 ||
|
||||
result->elemtype != INT2OID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
||||
errmsg("invalid int2vector data")));
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
@@ -194,14 +234,7 @@ int2vectorrecv(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
int2vectorsend(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16 *int2Array = (int16 *) PG_GETARG_POINTER(0);
|
||||
StringInfoData buf;
|
||||
int slot;
|
||||
|
||||
pq_begintypsend(&buf);
|
||||
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
||||
pq_sendint(&buf, int2Array[slot], sizeof(int16));
|
||||
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||||
return array_send(fcinfo);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -211,10 +244,12 @@ int2vectorsend(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
int2vectoreq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
int16 *arg1 = (int16 *) PG_GETARG_POINTER(0);
|
||||
int16 *arg2 = (int16 *) PG_GETARG_POINTER(1);
|
||||
int2vector *a = (int2vector *) PG_GETARG_POINTER(0);
|
||||
int2vector *b = (int2vector *) PG_GETARG_POINTER(1);
|
||||
|
||||
PG_RETURN_BOOL(memcmp(arg1, arg2, INDEX_MAX_KEYS * sizeof(int16)) == 0);
|
||||
if (a->dim1 != b->dim1)
|
||||
PG_RETURN_BOOL(false);
|
||||
PG_RETURN_BOOL(memcmp(a->values, b->values, a->dim1 * sizeof(int2)) == 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,20 +8,24 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/oid.c,v 1.61 2005/02/11 04:08:58 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/oid.c,v 1.62 2005/03/29 00:17:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "libpq/pqformat.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
|
||||
|
||||
#define OidVectorSize(n) (offsetof(oidvector, values) + (n) * sizeof(Oid))
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* USER I/O ROUTINES *
|
||||
*****************************************************************************/
|
||||
@@ -151,27 +155,54 @@ oidsend(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||||
}
|
||||
|
||||
/*
|
||||
* construct oidvector given a raw array of Oids
|
||||
*
|
||||
* If oids is NULL then caller must fill values[] afterward
|
||||
*/
|
||||
oidvector *
|
||||
buildoidvector(const Oid *oids, int n)
|
||||
{
|
||||
oidvector *result;
|
||||
|
||||
result = (oidvector *) palloc0(OidVectorSize(n));
|
||||
|
||||
if (n > 0 && oids)
|
||||
memcpy(result->values, oids, n * sizeof(Oid));
|
||||
|
||||
/*
|
||||
* Attach standard array header. For historical reasons, we set the
|
||||
* index lower bound to 0 not 1.
|
||||
*/
|
||||
result->size = OidVectorSize(n);
|
||||
result->ndim = 1;
|
||||
result->flags = 0;
|
||||
result->elemtype = OIDOID;
|
||||
result->dim1 = n;
|
||||
result->lbound1 = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* oidvectorin - converts "num num ..." to internal form
|
||||
*
|
||||
* Note:
|
||||
* Fills any unsupplied positions with InvalidOid.
|
||||
*/
|
||||
Datum
|
||||
oidvectorin(PG_FUNCTION_ARGS)
|
||||
{
|
||||
char *oidString = PG_GETARG_CSTRING(0);
|
||||
Oid *result = (Oid *) palloc(sizeof(Oid[INDEX_MAX_KEYS]));
|
||||
int slot;
|
||||
oidvector *result;
|
||||
int n;
|
||||
|
||||
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
||||
result = (oidvector *) palloc0(OidVectorSize(FUNC_MAX_ARGS));
|
||||
|
||||
for (n = 0; n < FUNC_MAX_ARGS; n++)
|
||||
{
|
||||
while (*oidString && isspace((unsigned char) *oidString))
|
||||
oidString++;
|
||||
if (*oidString == '\0')
|
||||
break;
|
||||
result[slot] = oidin_subr("oidvectorin", oidString, &oidString);
|
||||
result->values[n] = oidin_subr("oidvectorin", oidString, &oidString);
|
||||
}
|
||||
while (*oidString && isspace((unsigned char) *oidString))
|
||||
oidString++;
|
||||
@@ -179,8 +210,13 @@ oidvectorin(PG_FUNCTION_ARGS)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("oidvector has too many elements")));
|
||||
while (slot < INDEX_MAX_KEYS)
|
||||
result[slot++] = InvalidOid;
|
||||
|
||||
result->size = OidVectorSize(n);
|
||||
result->ndim = 1;
|
||||
result->flags = 0;
|
||||
result->elemtype = OIDOID;
|
||||
result->dim1 = n;
|
||||
result->lbound1 = 0;
|
||||
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
@@ -191,24 +227,19 @@ oidvectorin(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
oidvectorout(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *oidArray = (Oid *) PG_GETARG_POINTER(0);
|
||||
oidvector *oidArray = (oidvector *) PG_GETARG_POINTER(0);
|
||||
int num,
|
||||
maxnum;
|
||||
nnums = oidArray->dim1;
|
||||
char *rp;
|
||||
char *result;
|
||||
|
||||
/* find last non-zero value in vector */
|
||||
for (maxnum = INDEX_MAX_KEYS - 1; maxnum >= 0; maxnum--)
|
||||
if (oidArray[maxnum] != 0)
|
||||
break;
|
||||
|
||||
/* assumes sign, 10 digits, ' ' */
|
||||
rp = result = (char *) palloc((maxnum + 1) * 12 + 1);
|
||||
for (num = 0; num <= maxnum; num++)
|
||||
rp = result = (char *) palloc(nnums * 12 + 1);
|
||||
for (num = 0; num < nnums; num++)
|
||||
{
|
||||
if (num != 0)
|
||||
*rp++ = ' ';
|
||||
sprintf(rp, "%u", oidArray[num]);
|
||||
sprintf(rp, "%u", oidArray->values[num]);
|
||||
while (*++rp != '\0')
|
||||
;
|
||||
}
|
||||
@@ -223,11 +254,19 @@ Datum
|
||||
oidvectorrecv(PG_FUNCTION_ARGS)
|
||||
{
|
||||
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
||||
Oid *result = (Oid *) palloc(sizeof(Oid[INDEX_MAX_KEYS]));
|
||||
int slot;
|
||||
oidvector *result;
|
||||
|
||||
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
||||
result[slot] = (Oid) pq_getmsgint(buf, sizeof(Oid));
|
||||
result = (oidvector *)
|
||||
DatumGetPointer(DirectFunctionCall2(array_recv,
|
||||
PointerGetDatum(buf),
|
||||
ObjectIdGetDatum(OIDOID)));
|
||||
/* sanity checks: oidvector must be 1-D, no nulls */
|
||||
if (result->ndim != 1 ||
|
||||
result->flags != 0 ||
|
||||
result->elemtype != OIDOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
|
||||
errmsg("invalid oidvector data")));
|
||||
PG_RETURN_POINTER(result);
|
||||
}
|
||||
|
||||
@@ -237,14 +276,7 @@ oidvectorrecv(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
oidvectorsend(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *oidArray = (Oid *) PG_GETARG_POINTER(0);
|
||||
StringInfoData buf;
|
||||
int slot;
|
||||
|
||||
pq_begintypsend(&buf);
|
||||
for (slot = 0; slot < INDEX_MAX_KEYS; slot++)
|
||||
pq_sendint(&buf, oidArray[slot], sizeof(Oid));
|
||||
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
|
||||
return array_send(fcinfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -327,71 +359,49 @@ oidsmaller(PG_FUNCTION_ARGS)
|
||||
Datum
|
||||
oidvectoreq(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
||||
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
||||
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
|
||||
|
||||
PG_RETURN_BOOL(memcmp(arg1, arg2, INDEX_MAX_KEYS * sizeof(Oid)) == 0);
|
||||
PG_RETURN_BOOL(cmp == 0);
|
||||
}
|
||||
|
||||
Datum
|
||||
oidvectorne(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
||||
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
||||
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
|
||||
|
||||
PG_RETURN_BOOL(memcmp(arg1, arg2, INDEX_MAX_KEYS * sizeof(Oid)) != 0);
|
||||
PG_RETURN_BOOL(cmp != 0);
|
||||
}
|
||||
|
||||
Datum
|
||||
oidvectorlt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
||||
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
||||
int i;
|
||||
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
|
||||
|
||||
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
||||
if (arg1[i] != arg2[i])
|
||||
PG_RETURN_BOOL(arg1[i] < arg2[i]);
|
||||
PG_RETURN_BOOL(false);
|
||||
PG_RETURN_BOOL(cmp < 0);
|
||||
}
|
||||
|
||||
Datum
|
||||
oidvectorle(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
||||
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
||||
int i;
|
||||
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
|
||||
|
||||
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
||||
if (arg1[i] != arg2[i])
|
||||
PG_RETURN_BOOL(arg1[i] <= arg2[i]);
|
||||
PG_RETURN_BOOL(true);
|
||||
PG_RETURN_BOOL(cmp <= 0);
|
||||
}
|
||||
|
||||
Datum
|
||||
oidvectorge(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
||||
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
||||
int i;
|
||||
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
|
||||
|
||||
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
||||
if (arg1[i] != arg2[i])
|
||||
PG_RETURN_BOOL(arg1[i] >= arg2[i]);
|
||||
PG_RETURN_BOOL(true);
|
||||
PG_RETURN_BOOL(cmp >= 0);
|
||||
}
|
||||
|
||||
Datum
|
||||
oidvectorgt(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Oid *arg1 = (Oid *) PG_GETARG_POINTER(0);
|
||||
Oid *arg2 = (Oid *) PG_GETARG_POINTER(1);
|
||||
int i;
|
||||
int32 cmp = DatumGetInt32(btoidvectorcmp(fcinfo));
|
||||
|
||||
for (i = 0; i < INDEX_MAX_KEYS; i++)
|
||||
if (arg1[i] != arg2[i])
|
||||
PG_RETURN_BOOL(arg1[i] > arg2[i]);
|
||||
PG_RETURN_BOOL(false);
|
||||
PG_RETURN_BOOL(cmp > 0);
|
||||
}
|
||||
|
||||
Datum
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.92 2004/12/31 22:01:22 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.93 2005/03/29 00:17:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -98,7 +98,7 @@ regprocin(PG_FUNCTION_ARGS)
|
||||
CStringGetDatum(pro_name_or_oid));
|
||||
|
||||
hdesc = heap_openr(ProcedureRelationName, AccessShareLock);
|
||||
sysscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true,
|
||||
sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndex, true,
|
||||
SnapshotNow, 1, skey);
|
||||
|
||||
while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
|
||||
@@ -336,7 +336,7 @@ format_procedure(Oid procedure_oid)
|
||||
quote_qualified_identifier(nspname, proname));
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
Oid thisargtype = procform->proargtypes[i];
|
||||
Oid thisargtype = procform->proargtypes.values[i];
|
||||
|
||||
if (i > 0)
|
||||
appendStringInfoChar(&buf, ',');
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.188 2005/01/13 17:19:10 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.189 2005/03/29 00:17:08 tgl Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@@ -553,9 +553,10 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
val = (bytea *) fastgetattr(ht_trig,
|
||||
Anum_pg_trigger_tgargs,
|
||||
tgrel->rd_att, &isnull);
|
||||
val = (bytea *)
|
||||
DatumGetPointer(fastgetattr(ht_trig,
|
||||
Anum_pg_trigger_tgargs,
|
||||
tgrel->rd_att, &isnull));
|
||||
if (isnull)
|
||||
elog(ERROR, "tgargs is null for trigger %u", trigid);
|
||||
p = (char *) VARDATA(val);
|
||||
@@ -637,6 +638,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
|
||||
Oid indrelid;
|
||||
int keyno;
|
||||
Oid keycoltype;
|
||||
Datum indclassDatum;
|
||||
bool isnull;
|
||||
oidvector *indclass;
|
||||
StringInfoData buf;
|
||||
char *str;
|
||||
char *sep;
|
||||
@@ -654,6 +658,12 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
|
||||
indrelid = idxrec->indrelid;
|
||||
Assert(indexrelid == idxrec->indexrelid);
|
||||
|
||||
/* Must get indclass the hard way */
|
||||
indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
|
||||
Anum_pg_index_indclass, &isnull);
|
||||
Assert(!isnull);
|
||||
indclass = (oidvector *) DatumGetPointer(indclassDatum);
|
||||
|
||||
/*
|
||||
* Fetch the pg_class tuple of the index relation
|
||||
*/
|
||||
@@ -720,7 +730,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
|
||||
sep = "";
|
||||
for (keyno = 0; keyno < idxrec->indnatts; keyno++)
|
||||
{
|
||||
AttrNumber attnum = idxrec->indkey[keyno];
|
||||
AttrNumber attnum = idxrec->indkey.values[keyno];
|
||||
|
||||
if (!colno)
|
||||
appendStringInfo(&buf, sep);
|
||||
@@ -764,7 +774,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, int prettyFlags)
|
||||
* Add the operator class name
|
||||
*/
|
||||
if (!colno)
|
||||
get_opclass_name(idxrec->indclass[keyno], keycoltype,
|
||||
get_opclass_name(indclass->values[keyno], keycoltype,
|
||||
&buf);
|
||||
}
|
||||
|
||||
@@ -3537,7 +3547,10 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
|
||||
nargs = 0;
|
||||
foreach(l, expr->args)
|
||||
{
|
||||
Assert(nargs < FUNC_MAX_ARGS);
|
||||
if (nargs >= FUNC_MAX_ARGS)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
|
||||
errmsg("too many arguments")));
|
||||
argtypes[nargs] = exprType((Node *) lfirst(l));
|
||||
nargs++;
|
||||
}
|
||||
|
||||
31
src/backend/utils/cache/lsyscache.c
vendored
31
src/backend/utils/cache/lsyscache.c
vendored
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.120 2005/01/27 23:36:12 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.121 2005/03/29 00:17:11 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -773,15 +773,36 @@ get_func_rettype(Oid funcid)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_func_nargs
|
||||
* Given procedure id, return the number of arguments.
|
||||
*/
|
||||
int
|
||||
get_func_nargs(Oid funcid)
|
||||
{
|
||||
HeapTuple tp;
|
||||
int result;
|
||||
|
||||
tp = SearchSysCache(PROCOID,
|
||||
ObjectIdGetDatum(funcid),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tp))
|
||||
elog(ERROR, "cache lookup failed for function %u", funcid);
|
||||
|
||||
result = ((Form_pg_proc) GETSTRUCT(tp))->pronargs;
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* get_func_signature
|
||||
* Given procedure id, return the function's argument and result types.
|
||||
* (The return value is the result type.)
|
||||
*
|
||||
* argtypes must point to a vector of size FUNC_MAX_ARGS.
|
||||
* The arguments are returned as a palloc'd array.
|
||||
*/
|
||||
Oid
|
||||
get_func_signature(Oid funcid, Oid *argtypes, int *nargs)
|
||||
get_func_signature(Oid funcid, Oid **argtypes, int *nargs)
|
||||
{
|
||||
HeapTuple tp;
|
||||
Form_pg_proc procstruct;
|
||||
@@ -796,8 +817,10 @@ get_func_signature(Oid funcid, Oid *argtypes, int *nargs)
|
||||
procstruct = (Form_pg_proc) GETSTRUCT(tp);
|
||||
|
||||
result = procstruct->prorettype;
|
||||
memcpy(argtypes, procstruct->proargtypes, FUNC_MAX_ARGS * sizeof(Oid));
|
||||
*nargs = (int) procstruct->pronargs;
|
||||
Assert(*nargs == procstruct->proargtypes.dim1);
|
||||
*argtypes = (Oid *) palloc(*nargs * sizeof(Oid));
|
||||
memcpy(*argtypes, procstruct->proargtypes.values, *nargs * sizeof(Oid));
|
||||
|
||||
ReleaseSysCache(tp);
|
||||
return result;
|
||||
|
||||
117
src/backend/utils/cache/relcache.c
vendored
117
src/backend/utils/cache/relcache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.217 2005/03/28 00:58:26 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/relcache.c,v 1.218 2005/03/29 00:17:11 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -27,7 +27,6 @@
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
@@ -81,6 +80,7 @@ static FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
|
||||
static FormData_pg_attribute Desc_pg_attribute[Natts_pg_attribute] = {Schema_pg_attribute};
|
||||
static FormData_pg_attribute Desc_pg_proc[Natts_pg_proc] = {Schema_pg_proc};
|
||||
static FormData_pg_attribute Desc_pg_type[Natts_pg_type] = {Schema_pg_type};
|
||||
static FormData_pg_attribute Desc_pg_index[Natts_pg_index] = {Schema_pg_index};
|
||||
|
||||
/*
|
||||
* Hash tables that index the relation cache
|
||||
@@ -267,10 +267,11 @@ static void RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
||||
static Relation RelationBuildDesc(RelationBuildDescInfo buildinfo,
|
||||
Relation oldrelation);
|
||||
static void RelationInitPhysicalAddr(Relation relation);
|
||||
static TupleDesc GetPgIndexDescriptor(void);
|
||||
static void AttrDefaultFetch(Relation relation);
|
||||
static void CheckConstraintFetch(Relation relation);
|
||||
static List *insert_ordered_oid(List *list, Oid datum);
|
||||
static void IndexSupportInitialize(Form_pg_index iform,
|
||||
static void IndexSupportInitialize(oidvector *indclass,
|
||||
Oid *indexOperator,
|
||||
RegProcedure *indexSupport,
|
||||
StrategyNumber maxStrategyNumber,
|
||||
@@ -918,6 +919,8 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
{
|
||||
HeapTuple tuple;
|
||||
Form_pg_am aform;
|
||||
Datum indclassDatum;
|
||||
bool isnull;
|
||||
MemoryContext indexcxt;
|
||||
MemoryContext oldcontext;
|
||||
Oid *operator;
|
||||
@@ -945,6 +948,18 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
/*
|
||||
* indclass cannot be referenced directly through the C struct, because
|
||||
* it is after the variable-width indkey field. Therefore we extract
|
||||
* the datum the hard way and provide a direct link in the relcache.
|
||||
*/
|
||||
indclassDatum = fastgetattr(relation->rd_indextuple,
|
||||
Anum_pg_index_indclass,
|
||||
GetPgIndexDescriptor(),
|
||||
&isnull);
|
||||
Assert(!isnull);
|
||||
relation->rd_indclass = (oidvector *) DatumGetPointer(indclassDatum);
|
||||
|
||||
/*
|
||||
* Make a copy of the pg_am entry for the index's access method
|
||||
*/
|
||||
@@ -1014,7 +1029,7 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
* Fill the operator and support procedure OID arrays. (supportinfo is
|
||||
* left as zeroes, and is filled on-the-fly when used)
|
||||
*/
|
||||
IndexSupportInitialize(relation->rd_index,
|
||||
IndexSupportInitialize(relation->rd_indclass,
|
||||
operator, support,
|
||||
amstrategies, amsupport, natts);
|
||||
|
||||
@@ -1028,7 +1043,7 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
/*
|
||||
* IndexSupportInitialize
|
||||
* Initializes an index's cached opclass information,
|
||||
* given the index's pg_index tuple.
|
||||
* given the index's pg_index.indclass entry.
|
||||
*
|
||||
* Data is returned into *indexOperator and *indexSupport, which are arrays
|
||||
* allocated by the caller.
|
||||
@@ -1040,7 +1055,7 @@ RelationInitIndexAccessInfo(Relation relation)
|
||||
* access method.
|
||||
*/
|
||||
static void
|
||||
IndexSupportInitialize(Form_pg_index iform,
|
||||
IndexSupportInitialize(oidvector *indclass,
|
||||
Oid *indexOperator,
|
||||
RegProcedure *indexSupport,
|
||||
StrategyNumber maxStrategyNumber,
|
||||
@@ -1049,19 +1064,15 @@ IndexSupportInitialize(Form_pg_index iform,
|
||||
{
|
||||
int attIndex;
|
||||
|
||||
/*
|
||||
* XXX note that the following assumes the INDEX tuple is well formed
|
||||
* and that the *key and *class are 0 terminated.
|
||||
*/
|
||||
for (attIndex = 0; attIndex < maxAttributeNumber; attIndex++)
|
||||
{
|
||||
OpClassCacheEnt *opcentry;
|
||||
|
||||
if (!OidIsValid(iform->indclass[attIndex]))
|
||||
if (!OidIsValid(indclass->values[attIndex]))
|
||||
elog(ERROR, "bogus pg_index tuple");
|
||||
|
||||
/* look up the info for this opclass, using a cache */
|
||||
opcentry = LookupOpclassInfo(iform->indclass[attIndex],
|
||||
opcentry = LookupOpclassInfo(indclass->values[attIndex],
|
||||
maxStrategyNumber,
|
||||
maxSupportNumber);
|
||||
|
||||
@@ -2479,6 +2490,53 @@ RelationCacheInitializePhase3(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* GetPgIndexDescriptor -- get a predefined tuple descriptor for pg_index
|
||||
*
|
||||
* We need this kluge because we have to be able to access non-fixed-width
|
||||
* fields of pg_index before we have the standard catalog caches available.
|
||||
* We use predefined data that's set up in just the same way as the
|
||||
* bootstrapped reldescs used by formrdesc(). The resulting tupdesc is
|
||||
* not 100% kosher: it does not have the correct relation OID in attrelid,
|
||||
* nor does it have a TupleConstr field. But it's good enough for the
|
||||
* purpose of extracting fields.
|
||||
*/
|
||||
static TupleDesc
|
||||
GetPgIndexDescriptor(void)
|
||||
{
|
||||
static TupleDesc pgindexdesc = NULL;
|
||||
MemoryContext oldcxt;
|
||||
int i;
|
||||
|
||||
/* Already done? */
|
||||
if (pgindexdesc)
|
||||
return pgindexdesc;
|
||||
|
||||
oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
|
||||
|
||||
pgindexdesc = CreateTemplateTupleDesc(Natts_pg_index, false);
|
||||
pgindexdesc->tdtypeid = RECORDOID; /* not right, but we don't care */
|
||||
pgindexdesc->tdtypmod = -1;
|
||||
|
||||
for (i = 0; i < Natts_pg_index; i++)
|
||||
{
|
||||
memcpy(pgindexdesc->attrs[i],
|
||||
&Desc_pg_index[i],
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
/* make sure attcacheoff is valid */
|
||||
pgindexdesc->attrs[i]->attcacheoff = -1;
|
||||
}
|
||||
|
||||
/* initialize first attribute's attcacheoff, cf RelationBuildTupleDesc */
|
||||
pgindexdesc->attrs[0]->attcacheoff = 0;
|
||||
|
||||
/* Note: we don't bother to set up a TupleConstr entry */
|
||||
|
||||
MemoryContextSwitchTo(oldcxt);
|
||||
|
||||
return pgindexdesc;
|
||||
}
|
||||
|
||||
static void
|
||||
AttrDefaultFetch(Relation relation)
|
||||
{
|
||||
@@ -2773,15 +2831,11 @@ RelationGetIndexExpressions(Relation relation)
|
||||
* After successfully completing the work, we copy it into the
|
||||
* relcache entry. This avoids problems if we get some sort of error
|
||||
* partway through.
|
||||
*
|
||||
* We make use of the syscache's copy of pg_index's tupledesc to access
|
||||
* the non-fixed fields of the tuple. We assume that the syscache
|
||||
* will be initialized before any access of a partial index could
|
||||
* occur. (This would probably fail if we were to allow partial
|
||||
* indexes on system catalogs.)
|
||||
*/
|
||||
exprsDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple,
|
||||
Anum_pg_index_indexprs, &isnull);
|
||||
exprsDatum = heap_getattr(relation->rd_indextuple,
|
||||
Anum_pg_index_indexprs,
|
||||
GetPgIndexDescriptor(),
|
||||
&isnull);
|
||||
Assert(!isnull);
|
||||
exprsString = DatumGetCString(DirectFunctionCall1(textout, exprsDatum));
|
||||
result = (List *) stringToNode(exprsString);
|
||||
@@ -2845,15 +2899,11 @@ RelationGetIndexPredicate(Relation relation)
|
||||
* After successfully completing the work, we copy it into the
|
||||
* relcache entry. This avoids problems if we get some sort of error
|
||||
* partway through.
|
||||
*
|
||||
* We make use of the syscache's copy of pg_index's tupledesc to access
|
||||
* the non-fixed fields of the tuple. We assume that the syscache
|
||||
* will be initialized before any access of a partial index could
|
||||
* occur. (This would probably fail if we were to allow partial
|
||||
* indexes on system catalogs.)
|
||||
*/
|
||||
predDatum = SysCacheGetAttr(INDEXRELID, relation->rd_indextuple,
|
||||
Anum_pg_index_indpred, &isnull);
|
||||
predDatum = heap_getattr(relation->rd_indextuple,
|
||||
Anum_pg_index_indpred,
|
||||
GetPgIndexDescriptor(),
|
||||
&isnull);
|
||||
Assert(!isnull);
|
||||
predString = DatumGetCString(DirectFunctionCall1(textout, predDatum));
|
||||
result = (List *) stringToNode(predString);
|
||||
@@ -2990,6 +3040,8 @@ load_relcache_init_file(void)
|
||||
Relation rel;
|
||||
Form_pg_class relform;
|
||||
bool has_not_null;
|
||||
Datum indclassDatum;
|
||||
bool isnull;
|
||||
|
||||
/* first read the relation descriptor length */
|
||||
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
|
||||
@@ -3081,6 +3133,14 @@ load_relcache_init_file(void)
|
||||
rel->rd_indextuple->t_data = (HeapTupleHeader) ((char *) rel->rd_indextuple + HEAPTUPLESIZE);
|
||||
rel->rd_index = (Form_pg_index) GETSTRUCT(rel->rd_indextuple);
|
||||
|
||||
/* fix up indclass pointer too */
|
||||
indclassDatum = fastgetattr(rel->rd_indextuple,
|
||||
Anum_pg_index_indclass,
|
||||
GetPgIndexDescriptor(),
|
||||
&isnull);
|
||||
Assert(!isnull);
|
||||
rel->rd_indclass = (oidvector *) DatumGetPointer(indclassDatum);
|
||||
|
||||
/* next, read the access method tuple form */
|
||||
if ((nread = fread(&len, 1, sizeof(len), fp)) != sizeof(len))
|
||||
goto read_failed;
|
||||
@@ -3133,6 +3193,7 @@ load_relcache_init_file(void)
|
||||
|
||||
Assert(rel->rd_index == NULL);
|
||||
Assert(rel->rd_indextuple == NULL);
|
||||
Assert(rel->rd_indclass == NULL);
|
||||
Assert(rel->rd_am == NULL);
|
||||
Assert(rel->rd_indexcxt == NULL);
|
||||
Assert(rel->rd_operator == NULL);
|
||||
|
||||
12
src/backend/utils/cache/syscache.c
vendored
12
src/backend/utils/cache/syscache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.96 2004/12/31 22:01:25 pgsql Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.97 2005/03/29 00:17:12 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* These routines allow the parser/planner/executor to perform
|
||||
@@ -334,15 +334,15 @@ static const struct cachedesc cacheinfo[] = {
|
||||
0,
|
||||
0
|
||||
}},
|
||||
{ProcedureRelationName, /* PROCNAMENSP */
|
||||
ProcedureNameNspIndex,
|
||||
{ProcedureRelationName, /* PROCNAMEARGSNSP */
|
||||
ProcedureNameArgsNspIndex,
|
||||
0,
|
||||
4,
|
||||
3,
|
||||
{
|
||||
Anum_pg_proc_proname,
|
||||
Anum_pg_proc_pronargs,
|
||||
Anum_pg_proc_proargtypes,
|
||||
Anum_pg_proc_pronamespace
|
||||
Anum_pg_proc_pronamespace,
|
||||
0
|
||||
}},
|
||||
{ProcedureRelationName, /* PROCOID */
|
||||
ProcedureOidIndex,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.90 2005/03/22 20:13:07 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/fmgr/fmgr.c,v 1.91 2005/03/29 00:17:15 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -344,7 +344,7 @@ fmgr_info_C_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple)
|
||||
for (i = 0; i < procedureStruct->pronargs; i++)
|
||||
{
|
||||
fnextra->arg_toastable[i] =
|
||||
TypeIsToastable(procedureStruct->proargtypes[i]);
|
||||
TypeIsToastable(procedureStruct->proargtypes.values[i]);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
|
||||
Reference in New Issue
Block a user