diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 5de44d6..e343635 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -2620,7 +2620,7 @@
Return Type
Description
Example
- Result
+ Result
@@ -2641,32 +2641,6 @@
- get_bit(string, offset)
- int
-
- Extract bit from string
-
- get_bit
-
-
- get_bit(E'Th\\000omas'::bytea, 45)
- 1
-
-
-
- get_byte(string, offset)
- int
-
- Extract byte from string
-
- get_byte
-
-
- get_byte(E'Th\\000omas'::bytea, 4)
- 109
-
-
-
octet_length(string)
int
Number of bytes in binary string
@@ -2675,39 +2649,21 @@
- position(substring in string)
- int
- Location of specified substring
- position(E'\\000om'::bytea in E'Th\\000omas'::bytea)
- 3
-
-
-
- set_bit(string,
- offset, newvalue>)
+ overlay(string placing string from int for int)
bytea
- Set bit in string
-
- set_bit
-
+ Replace substring
- set_bit(E'Th\\000omas'::bytea, 45, 0)
- Th\000omAs
+ overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 2 for 3)
+ T\\002\\003mas
- set_byte(string,
- offset, newvalue>)
- bytea
-
- Set byte in string
-
- set_byte
-
-
- set_byte(E'Th\\000omas'::bytea, 4, 64)
- Th\000o@as
+ position(substring in string)
+ int
+ Location of specified substring
+ position(E'\\000om'::bytea in E'Th\\000omas'::bytea)
+ 3
@@ -2784,7 +2740,7 @@
bytea
- Decode binary string from string previously
+ Decode binary string from string previously
encoded with encode>. Parameter type is same as in encode>.
decode(E'123\\000456', 'escape')
@@ -2805,6 +2761,36 @@
123\000456
+
+
+ get_bit(string, offset)
+
+ int
+
+ Extract bit from string
+
+ get_bit
+
+
+ get_bit(E'Th\\000omas'::bytea, 45)
+ 1
+
+
+
+
+ get_byte(string, offset)
+
+ int
+
+ Extract byte from string
+
+ get_byte
+
+
+ get_byte(E'Th\\000omas'::bytea, 4)
+ 109
+
+
length(string)
int
@@ -2834,6 +2820,38 @@
md5(E'Th\\000omas'::bytea)
8ab2d3c9689aaf18 b4958c334c82d8b1
+
+
+
+ set_bit(string,
+ offset, newvalue>)
+
+ bytea
+
+ Set bit in string
+
+ set_bit
+
+
+ set_bit(E'Th\\000omas'::bytea, 45, 0)
+ Th\000omAs
+
+
+
+
+ set_byte(string,
+ offset, newvalue>)
+
+ bytea
+
+ Set byte in string
+
+ set_byte
+
+
+ set_byte(E'Th\\000omas'::bytea, 4, 64)
+ Th\000o@as
+
@@ -2934,7 +2952,15 @@
bit_length,
octet_length,
position,
- substring.
+ substring,
+ overlay.
+
+
+
+ The following functions work on bit strings as well as binary
+ strings:
+ get_bit,
+ set_bit.
diff --git a/doc/src/sgml/ref/prepare_transaction.sgml b/doc/src/sgml/ref/prepare_transaction.sgml
index 0a28092..8f95e29 100644
--- a/doc/src/sgml/ref/prepare_transaction.sgml
+++ b/doc/src/sgml/ref/prepare_transaction.sgml
@@ -83,6 +83,15 @@ PREPARE TRANSACTION transaction_id
Notes
+ PREPARE TRANSACTION> is not intended for use in applications
+ or interactive sessions. It's purpose is to allow an external
+ transaction manager to perform atomic global transactions across multiple
+ databases or other transactional resources. Unless you're writing a
+ transaction manager, you probably shouldn't be using PREPARE
+ TRANSACTION>.
+
+
+
This command must be used inside a transaction block. Use to start one.
diff --git a/src/backend/access/gin/ginbulk.c b/src/backend/access/gin/ginbulk.c
index 954884d..e043713 100644
--- a/src/backend/access/gin/ginbulk.c
+++ b/src/backend/access/gin/ginbulk.c
@@ -22,15 +22,60 @@
#define DEF_NENTRY 2048
#define DEF_NPTR 4
+static void*
+ginAppendData(void *old, void *new, void *arg)
+{
+ EntryAccumulator *eo = (EntryAccumulator*)old,
+ *en = (EntryAccumulator*)new;
+
+ BuildAccumulator *accum = (BuildAccumulator*)arg;
+
+ if (eo->number >= eo->length)
+ {
+ accum->allocatedMemory -= GetMemoryChunkSpace(eo->list);
+ eo->length *= 2;
+ eo->list = (ItemPointerData *) repalloc(eo->list,
+ sizeof(ItemPointerData) * eo->length);
+ accum->allocatedMemory += GetMemoryChunkSpace(eo->list);
+ }
+
+ /* If item pointers are not ordered, they will need to be sorted. */
+ if (eo->shouldSort == FALSE)
+ {
+ int res;
+
+ res = compareItemPointers(eo->list + eo->number - 1, en->list);
+ Assert(res != 0);
+
+ if (res > 0)
+ eo->shouldSort = TRUE;
+ }
+
+ eo->list[eo->number] = en->list[0];
+ eo->number++;
+
+ return old;
+}
+
+static int
+cmpEntryAccumulator(const void *a, const void *b, void *arg)
+{
+ EntryAccumulator *ea = (EntryAccumulator*)a;
+ EntryAccumulator *eb = (EntryAccumulator*)b;
+ BuildAccumulator *accum = (BuildAccumulator*)arg;
+
+ return compareAttEntries(accum->ginstate, ea->attnum, ea->value,
+ eb->attnum, eb->value);
+}
+
void
ginInitBA(BuildAccumulator *accum)
{
- accum->maxdepth = 1;
- accum->stackpos = 0;
- accum->entries = NULL;
- accum->stack = NULL;
accum->allocatedMemory = 0;
accum->entryallocator = NULL;
+ accum->tree = rb_create(cmpEntryAccumulator, ginAppendData, NULL, accum);
+ accum->iterator = NULL;
+ accum->tmpList = NULL;
}
static EntryAccumulator *
@@ -48,36 +93,6 @@ EAAllocate(BuildAccumulator *accum)
}
/*
- * Stores heap item pointer. For robust, it checks that
- * item pointer are ordered
- */
-static void
-ginInsertData(BuildAccumulator *accum, EntryAccumulator *entry, ItemPointer heapptr)
-{
- if (entry->number >= entry->length)
- {
- accum->allocatedMemory -= GetMemoryChunkSpace(entry->list);
- entry->length *= 2;
- entry->list = (ItemPointerData *) repalloc(entry->list,
- sizeof(ItemPointerData) * entry->length);
- accum->allocatedMemory += GetMemoryChunkSpace(entry->list);
- }
-
- if (entry->shouldSort == FALSE)
- {
- int res = compareItemPointers(entry->list + entry->number - 1, heapptr);
-
- Assert(res != 0);
-
- if (res > 0)
- entry->shouldSort = TRUE;
- }
-
- entry->list[entry->number] = *heapptr;
- entry->number++;
-}
-
-/*
* This is basically the same as datumCopy(), but modified to count
* palloc'd space in accum.
*/
@@ -103,57 +118,38 @@ getDatumCopy(BuildAccumulator *accum, OffsetNumber attnum, Datum value)
static void
ginInsertEntry(BuildAccumulator *accum, ItemPointer heapptr, OffsetNumber attnum, Datum entry)
{
- EntryAccumulator *ea = accum->entries,
- *pea = NULL;
- int res = 0;
- uint32 depth = 1;
+ EntryAccumulator *key = EAAllocate(accum),
+ *ea;
- while (ea)
- {
- res = compareAttEntries(accum->ginstate, attnum, entry, ea->attnum, ea->value);
- if (res == 0)
- break; /* found */
- else
- {
- pea = ea;
- if (res < 0)
- ea = ea->left;
- else
- ea = ea->right;
- }
- depth++;
- }
+ key->attnum = attnum;
+ key->value = entry;
+ if (accum->tmpList == NULL)
+ accum->tmpList =
+ (ItemPointerData *) palloc(sizeof(ItemPointerData) * DEF_NPTR);
+ key->list = accum->tmpList;
+ key->list[0] = *heapptr;
- if (depth > accum->maxdepth)
- accum->maxdepth = depth;
+ ea = rb_insert(accum->tree, key);
if (ea == NULL)
{
- ea = EAAllocate(accum);
-
- ea->left = ea->right = NULL;
- ea->attnum = attnum;
- ea->value = getDatumCopy(accum, attnum, entry);
- ea->length = DEF_NPTR;
- ea->number = 1;
- ea->shouldSort = FALSE;
- ea->list = (ItemPointerData *) palloc(sizeof(ItemPointerData) * DEF_NPTR);
- accum->allocatedMemory += GetMemoryChunkSpace(ea->list);
- ea->list[0] = *heapptr;
-
- if (pea == NULL)
- accum->entries = ea;
- else
- {
- Assert(res != 0);
- if (res < 0)
- pea->left = ea;
- else
- pea->right = ea;
- }
+ /*
+ * The key has been inserted, so continue initialization.
+ */
+ key->value = getDatumCopy(accum, attnum, entry);
+ key->length = DEF_NPTR;
+ key->number = 1;
+ key->shouldSort = FALSE;
+ accum->allocatedMemory += GetMemoryChunkSpace(key->list);
+ accum->tmpList = NULL;
}
else
- ginInsertData(accum, ea, heapptr);
+ {
+ /*
+ * The key has been appended, so reset
+ */
+ accum->length--;
+ }
}
/*
@@ -219,86 +215,16 @@ qsortCompareItemPointers(const void *a, const void *b)
return res;
}
-/*
- * walk on binary tree and returns ordered nodes
- */
-static EntryAccumulator *
-walkTree(BuildAccumulator *accum)
-{
- EntryAccumulator *entry = accum->stack[accum->stackpos];
-
- if (entry->list != NULL)
- {
- /* return entry itself: we already was at left sublink */
- return entry;
- }
- else if (entry->right && entry->right != accum->stack[accum->stackpos + 1])
- {
- /* go on right sublink */
- accum->stackpos++;
- entry = entry->right;
-
- /* find most-left value */
- for (;;)
- {
- accum->stack[accum->stackpos] = entry;
- if (entry->left)
- {
- accum->stackpos++;
- entry = entry->left;
- }
- else
- break;
- }
- }
- else
- {
- /* we already return all left subtree, itself and right subtree */
- if (accum->stackpos == 0)
- return 0;
- accum->stackpos--;
- return walkTree(accum);
- }
-
- return entry;
-}
-
ItemPointerData *
ginGetEntry(BuildAccumulator *accum, OffsetNumber *attnum, Datum *value, uint32 *n)
{
EntryAccumulator *entry;
ItemPointerData *list;
- if (accum->stack == NULL)
- {
- /* first call */
- accum->stack = palloc0(sizeof(EntryAccumulator *) * (accum->maxdepth + 1));
- accum->allocatedMemory += GetMemoryChunkSpace(accum->stack);
- entry = accum->entries;
-
- if (entry == NULL)
- return NULL;
-
- /* find most-left value */
- for (;;)
- {
- accum->stack[accum->stackpos] = entry;
- if (entry->left)
- {
- accum->stackpos++;
- entry = entry->left;
- }
- else
- break;
- }
- }
- else
- {
- accum->allocatedMemory -= GetMemoryChunkSpace(accum->stack[accum->stackpos]->list);
- pfree(accum->stack[accum->stackpos]->list);
- accum->stack[accum->stackpos]->list = NULL;
- entry = walkTree(accum);
- }
+ if (accum->iterator == NULL)
+ accum->iterator = rb_begin_iterate(accum->tree, LeftRightWalk);
+
+ entry = rb_iterate(accum->iterator);
if (entry == NULL)
return NULL;
diff --git a/src/backend/access/gin/ginfast.c b/src/backend/access/gin/ginfast.c
index 8d48cdf..3fb4441 100644
--- a/src/backend/access/gin/ginfast.c
+++ b/src/backend/access/gin/ginfast.c
@@ -765,8 +765,7 @@ ginInsertCleanup(Relation index, GinState *ginstate,
*/
if (GinPageGetOpaque(page)->rightlink == InvalidBlockNumber ||
(GinPageHasFullRow(page) &&
- (accum.allocatedMemory >= maintenance_work_mem * 1024L ||
- accum.maxdepth > GIN_MAX_TREE_DEPTH)))
+ (accum.allocatedMemory >= maintenance_work_mem * 1024L)))
{
ItemPointerData *list;
uint32 nlist;
diff --git a/src/backend/access/gin/gininsert.c b/src/backend/access/gin/gininsert.c
index 902e361..97fc417 100644
--- a/src/backend/access/gin/gininsert.c
+++ b/src/backend/access/gin/gininsert.c
@@ -247,9 +247,7 @@ ginBuildCallback(Relation index, HeapTuple htup, Datum *values,
&htup->t_self);
/* If we've maxed out our available memory, dump everything to the index */
- /* Also dump if the tree seems to be getting too unbalanced */
- if (buildstate->accum.allocatedMemory >= maintenance_work_mem * 1024L ||
- buildstate->accum.maxdepth > GIN_MAX_TREE_DEPTH)
+ if (buildstate->accum.allocatedMemory >= maintenance_work_mem * 1024L)
{
ItemPointerData *list;
Datum entry;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 0b61a59..f911c86 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -4142,6 +4142,10 @@ readTimeLineHistory(TimeLineID targetTLI)
char fline[MAXPGPATH];
FILE *fd;
+ /* Timeline 1 does not have a history file, so no need to check */
+ if (targetTLI == 1)
+ return list_make1_int((int) targetTLI);
+
if (InArchiveRecovery)
{
TLHistoryFileName(histfname, targetTLI);
@@ -4227,6 +4231,10 @@ existsTimeLineHistory(TimeLineID probeTLI)
char histfname[MAXFNAMELEN];
FILE *fd;
+ /* Timeline 1 does not have a history file, so no need to check */
+ if (probeTLI == 1)
+ return false;
+
if (InArchiveRecovery)
{
TLHistoryFileName(histfname, probeTLI);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3d3ae79..146fa56 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -9586,9 +9586,9 @@ func_expr: func_name '(' ')' over_clause
| OVERLAY '(' overlay_list ')'
{
/* overlay(A PLACING B FROM C FOR D) is converted to
- * substring(A, 1, C-1) || B || substring(A, C+1, C+D)
+ * overlay(A, B, C, D)
* overlay(A PLACING B FROM C) is converted to
- * substring(A, 1, C-1) || B || substring(A, C+1, C+char_length(B))
+ * overlay(A, B, C)
*/
FuncCall *n = makeNode(FuncCall);
n->funcname = SystemFuncName("overlay");
@@ -10150,6 +10150,7 @@ extract_arg:
* SQL99 defines the OVERLAY() function:
* o overlay(text placing text from int for int)
* o overlay(text placing text from int)
+ * and similarly for binary strings
*/
overlay_list:
a_expr overlay_placing substr_from substr_for
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 576b872..03aff75 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -23,8 +23,10 @@
#define HEXDIG(z) ((z)<10 ? ((z)+'0') : ((z)-10+'A'))
+static VarBit *bit_catenate(VarBit *arg1, VarBit *arg2);
static VarBit *bitsubstring(VarBit *arg, int32 s, int32 l,
bool length_not_specified);
+static VarBit *bit_overlay(VarBit *t1, VarBit *t2, int sp, int sl);
/* common code for bittypmodin and varbittypmodin */
@@ -877,6 +879,13 @@ bitcat(PG_FUNCTION_ARGS)
{
VarBit *arg1 = PG_GETARG_VARBIT_P(0);
VarBit *arg2 = PG_GETARG_VARBIT_P(1);
+
+ PG_RETURN_VARBIT_P(bit_catenate(arg1, arg2));
+}
+
+static VarBit *
+bit_catenate(VarBit *arg1, VarBit *arg2)
+{
VarBit *result;
int bitlen1,
bitlen2,
@@ -919,7 +928,7 @@ bitcat(PG_FUNCTION_ARGS)
}
}
- PG_RETURN_VARBIT_P(result);
+ return result;
}
/* bitsubstr
@@ -1034,6 +1043,67 @@ bitsubstring(VarBit *arg, int32 s, int32 l, bool length_not_specified)
return result;
}
+/*
+ * bitoverlay
+ * Replace specified substring of first string with second
+ *
+ * The SQL standard defines OVERLAY() in terms of substring and concatenation.
+ * This code is a direct implementation of what the standard says.
+ */
+Datum
+bitoverlay(PG_FUNCTION_ARGS)
+{
+ VarBit *t1 = PG_GETARG_VARBIT_P(0);
+ VarBit *t2 = PG_GETARG_VARBIT_P(1);
+ int sp = PG_GETARG_INT32(2); /* substring start position */
+ int sl = PG_GETARG_INT32(3); /* substring length */
+
+ PG_RETURN_VARBIT_P(bit_overlay(t1, t2, sp, sl));
+}
+
+Datum
+bitoverlay_no_len(PG_FUNCTION_ARGS)
+{
+ VarBit *t1 = PG_GETARG_VARBIT_P(0);
+ VarBit *t2 = PG_GETARG_VARBIT_P(1);
+ int sp = PG_GETARG_INT32(2); /* substring start position */
+ int sl;
+
+ sl = VARBITLEN(t2); /* defaults to length(t2) */
+ PG_RETURN_VARBIT_P(bit_overlay(t1, t2, sp, sl));
+}
+
+static VarBit *
+bit_overlay(VarBit *t1, VarBit *t2, int sp, int sl)
+{
+ VarBit *result;
+ VarBit *s1;
+ VarBit *s2;
+ int sp_pl_sl;
+
+ /*
+ * Check for possible integer-overflow cases. For negative sp,
+ * throw a "substring length" error because that's what should be
+ * expected according to the spec's definition of OVERLAY().
+ */
+ if (sp <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SUBSTRING_ERROR),
+ errmsg("negative substring length not allowed")));
+ sp_pl_sl = sp + sl;
+ if (sp_pl_sl <= sl)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ s1 = bitsubstring(t1, 1, sp-1, false);
+ s2 = bitsubstring(t1, sp_pl_sl, -1, true);
+ result = bit_catenate(s1, t2);
+ result = bit_catenate(result, s2);
+
+ return result;
+}
+
/* bitlength, bitoctetlength
* Return the length of a bit string
*/
@@ -1606,3 +1676,103 @@ bitposition(PG_FUNCTION_ARGS)
}
PG_RETURN_INT32(0);
}
+
+
+/*
+ * bitsetbit
+ *
+ * Given an instance of type 'bit' creates a new one with
+ * the Nth bit set to the given value.
+ *
+ * The bit location is specified left-to-right in a zero-based fashion
+ * consistent with the other get_bit and set_bit functions, but
+ * inconsistent with the standard substring, position, overlay functions
+ */
+Datum
+bitsetbit(PG_FUNCTION_ARGS)
+{
+ VarBit *arg1 = PG_GETARG_VARBIT_P(0);
+ int32 n = PG_GETARG_INT32(1);
+ int32 newBit = PG_GETARG_INT32(2);
+ VarBit *result;
+ int len,
+ bitlen;
+ bits8 *r,
+ *p;
+ int byteNo,
+ bitNo;
+
+ bitlen = VARBITLEN(arg1);
+ if (n < 0 || n >= bitlen)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("bit index %d out of valid range (0..%d)",
+ n, bitlen - 1)));
+ /*
+ * sanity check!
+ */
+ if (newBit != 0 && newBit != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("new bit must be 0 or 1")));
+
+ len = VARSIZE(arg1);
+ result = (VarBit *) palloc(len);
+ SET_VARSIZE(result, len);
+ VARBITLEN(result) = bitlen;
+
+ p = VARBITS(arg1);
+ r = VARBITS(result);
+
+ memcpy(r, p, VARBITBYTES(arg1));
+
+ byteNo = n / BITS_PER_BYTE;
+ bitNo = BITS_PER_BYTE - 1 - (n % BITS_PER_BYTE);
+
+ /*
+ * Update the byte.
+ */
+ if (newBit == 0)
+ r[byteNo] &= (~(1 << bitNo));
+ else
+ r[byteNo] |= (1 << bitNo);
+
+ PG_RETURN_VARBIT_P(result);
+}
+
+/*
+ * bitgetbit
+ *
+ * returns the value of the Nth bit of a bit array (0 or 1).
+ *
+ * The bit location is specified left-to-right in a zero-based fashion
+ * consistent with the other get_bit and set_bit functions, but
+ * inconsistent with the standard substring, position, overlay functions
+ */
+Datum
+bitgetbit(PG_FUNCTION_ARGS)
+{
+ VarBit *arg1 = PG_GETARG_VARBIT_P(0);
+ int32 n = PG_GETARG_INT32(1);
+ int bitlen;
+ bits8 *p;
+ int byteNo,
+ bitNo;
+
+ bitlen = VARBITLEN(arg1);
+ if (n < 0 || n >= bitlen)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("bit index %d out of valid range (0..%d)",
+ n, bitlen - 1)));
+
+ p = VARBITS(arg1);
+
+ byteNo = n / BITS_PER_BYTE;
+ bitNo = BITS_PER_BYTE - 1 - (n % BITS_PER_BYTE);
+
+ if (p[byteNo] & (1 << bitNo))
+ PG_RETURN_INT32(1);
+ else
+ PG_RETURN_INT32(0);
+}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 6ec0fe5..d4174c9 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -60,11 +60,19 @@ static int text_position(text *t1, text *t2);
static void text_position_setup(text *t1, text *t2, TextPositionState *state);
static int text_position_next(int start_pos, TextPositionState *state);
static void text_position_cleanup(TextPositionState *state);
+static text *text_catenate(text *t1, text *t2);
static text *text_substring(Datum str,
int32 start,
int32 length,
bool length_not_specified);
+static text *text_overlay(text *t1, text *t2, int sp, int sl);
static void appendStringInfoText(StringInfo str, const text *t);
+static bytea *bytea_catenate(bytea *t1, bytea *t2);
+static bytea *bytea_substring(Datum str,
+ int S,
+ int L,
+ bool length_not_specified);
+static bytea *bytea_overlay(bytea *t1, bytea *t2, int sp, int sl);
/*****************************************************************************
@@ -559,17 +567,31 @@ textcat(PG_FUNCTION_ARGS)
{
text *t1 = PG_GETARG_TEXT_PP(0);
text *t2 = PG_GETARG_TEXT_PP(1);
+
+ PG_RETURN_TEXT_P(text_catenate(t1, t2));
+}
+
+/*
+ * text_catenate
+ * Guts of textcat(), broken out so it can be used by other functions
+ *
+ * Arguments can be in short-header form, but not compressed or out-of-line
+ */
+static text *
+text_catenate(text *t1, text *t2)
+{
+ text *result;
int len1,
len2,
len;
- text *result;
char *ptr;
len1 = VARSIZE_ANY_EXHDR(t1);
+ len2 = VARSIZE_ANY_EXHDR(t2);
+
+ /* paranoia ... probably should throw error instead? */
if (len1 < 0)
len1 = 0;
-
- len2 = VARSIZE_ANY_EXHDR(t2);
if (len2 < 0)
len2 = 0;
@@ -586,7 +608,7 @@ textcat(PG_FUNCTION_ARGS)
if (len2 > 0)
memcpy(ptr + len1, VARDATA_ANY(t2), len2);
- PG_RETURN_TEXT_P(result);
+ return result;
}
/*
@@ -866,6 +888,67 @@ text_substring(Datum str, int32 start, int32 length, bool length_not_specified)
}
/*
+ * textoverlay
+ * Replace specified substring of first string with second
+ *
+ * The SQL standard defines OVERLAY() in terms of substring and concatenation.
+ * This code is a direct implementation of what the standard says.
+ */
+Datum
+textoverlay(PG_FUNCTION_ARGS)
+{
+ text *t1 = PG_GETARG_TEXT_PP(0);
+ text *t2 = PG_GETARG_TEXT_PP(1);
+ int sp = PG_GETARG_INT32(2); /* substring start position */
+ int sl = PG_GETARG_INT32(3); /* substring length */
+
+ PG_RETURN_TEXT_P(text_overlay(t1, t2, sp, sl));
+}
+
+Datum
+textoverlay_no_len(PG_FUNCTION_ARGS)
+{
+ text *t1 = PG_GETARG_TEXT_PP(0);
+ text *t2 = PG_GETARG_TEXT_PP(1);
+ int sp = PG_GETARG_INT32(2); /* substring start position */
+ int sl;
+
+ sl = text_length(PointerGetDatum(t2)); /* defaults to length(t2) */
+ PG_RETURN_TEXT_P(text_overlay(t1, t2, sp, sl));
+}
+
+static text *
+text_overlay(text *t1, text *t2, int sp, int sl)
+{
+ text *result;
+ text *s1;
+ text *s2;
+ int sp_pl_sl;
+
+ /*
+ * Check for possible integer-overflow cases. For negative sp,
+ * throw a "substring length" error because that's what should be
+ * expected according to the spec's definition of OVERLAY().
+ */
+ if (sp <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SUBSTRING_ERROR),
+ errmsg("negative substring length not allowed")));
+ sp_pl_sl = sp + sl;
+ if (sp_pl_sl <= sl)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ s1 = text_substring(PointerGetDatum(t1), 1, sp-1, false);
+ s2 = text_substring(PointerGetDatum(t1), sp_pl_sl, -1, true);
+ result = text_catenate(s1, t2);
+ result = text_catenate(result, s2);
+
+ return result;
+}
+
+/*
* textpos -
* Return the position of the specified substring.
* Implements the SQL92 POSITION() function.
@@ -1640,17 +1723,31 @@ byteacat(PG_FUNCTION_ARGS)
{
bytea *t1 = PG_GETARG_BYTEA_PP(0);
bytea *t2 = PG_GETARG_BYTEA_PP(1);
+
+ PG_RETURN_BYTEA_P(bytea_catenate(t1, t2));
+}
+
+/*
+ * bytea_catenate
+ * Guts of byteacat(), broken out so it can be used by other functions
+ *
+ * Arguments can be in short-header form, but not compressed or out-of-line
+ */
+static bytea *
+bytea_catenate(bytea *t1, bytea *t2)
+{
+ bytea *result;
int len1,
len2,
len;
- bytea *result;
char *ptr;
len1 = VARSIZE_ANY_EXHDR(t1);
+ len2 = VARSIZE_ANY_EXHDR(t2);
+
+ /* paranoia ... probably should throw error instead? */
if (len1 < 0)
len1 = 0;
-
- len2 = VARSIZE_ANY_EXHDR(t2);
if (len2 < 0)
len2 = 0;
@@ -1667,7 +1764,7 @@ byteacat(PG_FUNCTION_ARGS)
if (len2 > 0)
memcpy(ptr + len1, VARDATA_ANY(t2), len2);
- PG_RETURN_BYTEA_P(result);
+ return result;
}
#define PG_STR_GET_BYTEA(str_) \
@@ -1691,16 +1788,41 @@ byteacat(PG_FUNCTION_ARGS)
Datum
bytea_substr(PG_FUNCTION_ARGS)
{
- int S = PG_GETARG_INT32(1); /* start position */
+ PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0),
+ PG_GETARG_INT32(1),
+ PG_GETARG_INT32(2),
+ false));
+}
+
+/*
+ * bytea_substr_no_len -
+ * Wrapper to avoid opr_sanity failure due to
+ * one function accepting a different number of args.
+ */
+Datum
+bytea_substr_no_len(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BYTEA_P(bytea_substring(PG_GETARG_DATUM(0),
+ PG_GETARG_INT32(1),
+ -1,
+ true));
+}
+
+static bytea *
+bytea_substring(Datum str,
+ int S,
+ int L,
+ bool length_not_specified)
+{
int S1; /* adjusted start position */
int L1; /* adjusted substring length */
S1 = Max(S, 1);
- if (fcinfo->nargs == 2)
+ if (length_not_specified)
{
/*
- * Not passed a length - PG_GETARG_BYTEA_P_SLICE() grabs everything to
+ * Not passed a length - DatumGetByteaPSlice() grabs everything to
* the end of the string if we pass it a negative value for length.
*/
L1 = -1;
@@ -1708,7 +1830,7 @@ bytea_substr(PG_FUNCTION_ARGS)
else
{
/* end position */
- int E = S + PG_GETARG_INT32(2);
+ int E = S + L;
/*
* A negative value for L is the only way for the end position to be
@@ -1725,28 +1847,78 @@ bytea_substr(PG_FUNCTION_ARGS)
* string.
*/
if (E < 1)
- PG_RETURN_BYTEA_P(PG_STR_GET_BYTEA(""));
+ return PG_STR_GET_BYTEA("");
L1 = E - S1;
}
/*
* If the start position is past the end of the string, SQL99 says to
- * return a zero-length string -- PG_GETARG_TEXT_P_SLICE() will do that
+ * return a zero-length string -- DatumGetByteaPSlice() will do that
* for us. Convert to zero-based starting position
*/
- PG_RETURN_BYTEA_P(PG_GETARG_BYTEA_P_SLICE(0, S1 - 1, L1));
+ return DatumGetByteaPSlice(str, S1 - 1, L1);
}
/*
- * bytea_substr_no_len -
- * Wrapper to avoid opr_sanity failure due to
- * one function accepting a different number of args.
+ * byteaoverlay
+ * Replace specified substring of first string with second
+ *
+ * The SQL standard defines OVERLAY() in terms of substring and concatenation.
+ * This code is a direct implementation of what the standard says.
*/
Datum
-bytea_substr_no_len(PG_FUNCTION_ARGS)
+byteaoverlay(PG_FUNCTION_ARGS)
{
- return bytea_substr(fcinfo);
+ bytea *t1 = PG_GETARG_BYTEA_PP(0);
+ bytea *t2 = PG_GETARG_BYTEA_PP(1);
+ int sp = PG_GETARG_INT32(2); /* substring start position */
+ int sl = PG_GETARG_INT32(3); /* substring length */
+
+ PG_RETURN_BYTEA_P(bytea_overlay(t1, t2, sp, sl));
+}
+
+Datum
+byteaoverlay_no_len(PG_FUNCTION_ARGS)
+{
+ bytea *t1 = PG_GETARG_BYTEA_PP(0);
+ bytea *t2 = PG_GETARG_BYTEA_PP(1);
+ int sp = PG_GETARG_INT32(2); /* substring start position */
+ int sl;
+
+ sl = VARSIZE_ANY_EXHDR(t2); /* defaults to length(t2) */
+ PG_RETURN_BYTEA_P(bytea_overlay(t1, t2, sp, sl));
+}
+
+static bytea *
+bytea_overlay(bytea *t1, bytea *t2, int sp, int sl)
+{
+ bytea *result;
+ bytea *s1;
+ bytea *s2;
+ int sp_pl_sl;
+
+ /*
+ * Check for possible integer-overflow cases. For negative sp,
+ * throw a "substring length" error because that's what should be
+ * expected according to the spec's definition of OVERLAY().
+ */
+ if (sp <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SUBSTRING_ERROR),
+ errmsg("negative substring length not allowed")));
+ sp_pl_sl = sp + sl;
+ if (sp_pl_sl <= sl)
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("integer out of range")));
+
+ s1 = bytea_substring(PointerGetDatum(t1), 1, sp-1, false);
+ s2 = bytea_substring(PointerGetDatum(t1), sp_pl_sl, -1, true);
+ result = bytea_catenate(s1, t2);
+ result = bytea_catenate(result, s2);
+
+ return result;
}
/*
diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile
index 03a155c..e8866df 100644
--- a/src/backend/utils/misc/Makefile
+++ b/src/backend/utils/misc/Makefile
@@ -14,7 +14,8 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
-OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o
+OBJS = guc.o help_config.o pg_rusage.o ps_status.o superuser.o tzparser.o \
+ rbtree.o
# This location might depend on the installation directories. Therefore
# we can't subsitute it into pg_config.h.
diff --git a/src/backend/utils/misc/rbtree.c b/src/backend/utils/misc/rbtree.c
new file mode 100644
index 0000000..b10c669
--- /dev/null
+++ b/src/backend/utils/misc/rbtree.c
@@ -0,0 +1,814 @@
+/*-------------------------------------------------------------------------
+ *
+ * rbtree.c
+ * implementation for PostgreSQL generic Red-Black binary tree package
+ * Adopted from http://algolist.manual.ru/ds/rbtree.php
+ *
+ * This code comes from Thomas Niemann's "Sorting and Searching Algorithms:
+ * a Cookbook".
+ *
+ * See http://www.cs.auckland.ac.nz/software/AlgAnim/niemann/s_man.htm for
+ * license terms: "Source code, when part of a software project, may be used
+ * freely without reference to the author."
+ *
+ * Red-black trees are a type of balanced binary tree wherein (1) any child of
+ * a red node is always black, and (2) every path from root to leaf traverses
+ * an equal number of black nodes. From these properties, it follows that the
+ * longest path from root to leaf is only about twice as long as the shortest,
+ * so lookups are guaranteed to run in O(lg n) time.
+ *
+ * Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/nodes/rbtree.c,v 1.69 2008/01/01 19:45:50 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "utils/rbtree.h"
+
+/**********************************************************************
+ * Declarations *
+ **********************************************************************/
+
+/*
+ * Values for RBNode->iteratorState
+ */
+#define InitialState (0)
+#define FirstStepDone (1)
+#define SecondStepDone (2)
+#define ThirdStepDone (3)
+
+/*
+ * Colors of node
+ */
+#define BLACK (0)
+#define RED (1)
+
+typedef struct RBNode
+{
+ uint32 iteratorState:2,
+ color: 1 ,
+ unused: 29;
+ struct RBNode *left;
+ struct RBNode *right;
+ struct RBNode *parent;
+ void *data;
+} RBNode;
+
+struct RBTree
+{
+ RBNode *root;
+ rb_comparator comparator;
+ rb_appendator appendator;
+ rb_freefunc freefunc;
+ void *arg;
+};
+
+struct RBTreeIterator
+{
+ RBNode *node;
+ void *(*iterate) (RBTreeIterator *iterator);
+};
+
+/*
+ * all leafs are sentinels, use castimized NIL name to prevent
+ * collision with sytem-wide NIL which is actually NULL
+ */
+#define RBNIL &sentinel
+
+RBNode sentinel = {InitialState, BLACK, 0, RBNIL, RBNIL, NULL, NULL};
+
+/**********************************************************************
+ * Create/free *
+ **********************************************************************/
+
+RBTree *
+rb_create(rb_comparator comparator, rb_appendator appendator,
+ rb_freefunc freefunc, void *arg)
+{
+ RBTree *tree = palloc(sizeof(RBTree));
+
+ tree->root = RBNIL;
+ tree->comparator = comparator;
+ tree->appendator = appendator;
+ tree->freefunc = freefunc;
+ tree->arg = arg;
+
+ return tree;
+}
+
+static void
+rb_free_recursive(RBNode *node, rb_freefunc freefunc)
+{
+ if (node->left != RBNIL)
+ rb_free_recursive(node->left, freefunc);
+ if (node->right != RBNIL)
+ rb_free_recursive(node->right, freefunc);
+ if (freefunc && node->data)
+ freefunc(node->data);
+ pfree(node);
+}
+
+void
+rb_free(RBTree *rb)
+{
+ if (!rb)
+ return;
+
+ if (rb->root != RBNIL)
+ rb_free_recursive(rb->root, rb->freefunc);
+
+ pfree(rb);
+}
+
+/**********************************************************************
+ * Search *
+ **********************************************************************/
+
+void *
+rb_find(RBTree *rb, void *data)
+{
+ RBNode *node = rb->root;
+ int cmp;
+
+ while (node != RBNIL)
+ {
+ cmp = rb->comparator(data, node->data, rb->arg);
+
+ if (cmp == 0)
+ return node->data;
+ else if (cmp < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+
+ return NULL;
+}
+
+/**********************************************************************
+ * Insertion *
+ **********************************************************************/
+
+/*
+ * Rotate node x to left.
+ *
+ * x's right child takes its place in the tree, and x becomes the left
+ * child of that node.
+ */
+static void
+rb_rotate_left(RBTree *rb, RBNode *x)
+{
+ RBNode *y = x->right;
+
+ /* establish x->right link */
+ x->right = y->left;
+ if (y->left != RBNIL)
+ y->left->parent = x;
+
+ /* establish y->parent link */
+ if (y != RBNIL)
+ y->parent = x->parent;
+ if (x->parent)
+ {
+ if (x == x->parent->left)
+ x->parent->left = y;
+ else
+ x->parent->right = y;
+ }
+ else
+ {
+ rb->root = y;
+ }
+
+ /* link x and y */
+ y->left = x;
+ if (x != RBNIL)
+ x->parent = y;
+}
+
+/*
+ * Rotate node x to right.
+ *
+ * x's left right child takes its place in the tree, and x becomes the right
+ * child of that node.
+ */
+static void
+rb_rotate_right(RBTree *rb, RBNode *x)
+{
+ RBNode *y = x->left;
+
+ /* establish x->left link */
+ x->left = y->right;
+ if (y->right != RBNIL)
+ y->right->parent = x;
+
+ /* establish y->parent link */
+ if (y != RBNIL)
+ y->parent = x->parent;
+ if (x->parent)
+ {
+ if (x == x->parent->right)
+ x->parent->right = y;
+ else
+ x->parent->left = y;
+ }
+ else
+ {
+ rb->root = y;
+ }
+
+ /* link x and y */
+ y->right = x;
+ if (x != RBNIL)
+ x->parent = y;
+}
+
+/*
+ * Maintain Red-Black tree balance after inserting node x.
+ *
+ * The newly inserted node is always initially marked red. That may lead to
+ * a situation where a red node has a red child, which is prohibited. We can
+ * always fix the problem by a series of color changes and/or "rotations",
+ * which move the problem progressively higher up in the tree. If one of the
+ * two red nodes is the root, we can always fix the problem by changing the
+ * root from red to black.
+ *
+ * (This does not work lower down in the tree because we must also maintain
+ * the invariant that every leaf has equal black-height.)
+ */
+static void
+rb_insert_fixup(RBTree *rb, RBNode *x)
+{
+ /*
+ * x is always a red node. Initially, it is the newly inserted node.
+ * Each iteration of this loop moves it higher up in the tree.
+ */
+ while (x != rb->root && x->parent->color == RED)
+ {
+ /*
+ * x and x->parent are both red. Fix depends on whether x->parent is
+ * a left or right child. In either case, we define y to be the
+ * "uncle" of x, that is, the other child of x's grandparent.
+ *
+ * If the uncle is red, we flip the grandparent to red and its two
+ * children to black. Then we loop around again to check whether the
+ * grandparent still has a problem.
+ *
+ * If the uncle is black, we will perform one or two "rotations" to
+ * balance the tree. Either x or x->parent will take the grandparent's
+ * position in the tree and recolored black, and the original
+ * grandparent will be recolored red and become a child of that node.
+ * This always leaves us with a valid red-black tree, so the loop
+ * will terminate.
+ */
+ if (x->parent == x->parent->parent->left)
+ {
+ RBNode *y = x->parent->parent->right;
+
+ if (y->color == RED)
+ {
+ /* uncle is RED */
+ x->parent->color = BLACK;
+ y->color = BLACK;
+ x->parent->parent->color = RED;
+ x = x->parent->parent;
+ }
+ else
+ {
+ /* uncle is BLACK */
+ if (x == x->parent->right)
+ {
+ /* make x a left child */
+ x = x->parent;
+ rb_rotate_left(rb, x);
+ }
+
+ /* recolor and rotate */
+ x->parent->color = BLACK;
+ x->parent->parent->color = RED;
+ rb_rotate_right(rb, x->parent->parent);
+ }
+ }
+ else
+ {
+ /* mirror image of above code */
+ RBNode *y = x->parent->parent->left;
+
+ if (y->color == RED)
+ {
+ /* uncle is RED */
+ x->parent->color = BLACK;
+ y->color = BLACK;
+ x->parent->parent->color = RED;
+ x = x->parent->parent;
+ }
+ else
+ {
+ /* uncle is BLACK */
+ if (x == x->parent->left)
+ {
+ x = x->parent;
+ rb_rotate_right(rb, x);
+ }
+ x->parent->color = BLACK;
+ x->parent->parent->color = RED;
+ rb_rotate_left(rb, x->parent->parent);
+ }
+ }
+ }
+
+ /*
+ * The root may already have been black; if not, the black-height of every
+ * node in the tree increases by one.
+ */
+ rb->root->color = BLACK;
+}
+
+/*
+ * Allocate node for data and insert in tree.
+ *
+ * Return old data (or result of appendator method) if it exists and NULL
+ * otherwise.
+ */
+void *
+rb_insert(RBTree *rb, void *data)
+{
+ RBNode *current,
+ *parent,
+ *x;
+ int cmp;
+
+ /* find where node belongs */
+ current = rb->root;
+ parent = NULL;
+ while (current != RBNIL)
+ {
+ cmp = rb->comparator(data, current->data, rb->arg);
+ if (cmp == 0)
+ {
+ /*
+ * Found node with given key. If appendator method is provided,
+ * call it to join old and new data; else, new data replaces old
+ * data.
+ */
+ if (rb->appendator)
+ {
+ current->data = rb->appendator(current->data, data, rb->arg);
+ return current->data;
+ }
+ else
+ {
+ void *old = current->data;
+
+ current->data = data;
+ return old;
+ }
+ }
+ parent = current;
+ current = (cmp < 0) ? current->left : current->right;
+ }
+
+ /* setup new node in tree */
+ x = palloc(sizeof(RBNode));
+ x->data = data;
+ x->parent = parent;
+ x->left = RBNIL;
+ x->right = RBNIL;
+ x->color = RED;
+ x->iteratorState = InitialState;
+
+ /* insert node in tree */
+ if (parent)
+ {
+ if (cmp < 0)
+ parent->left = x;
+ else
+ parent->right = x;
+ }
+ else
+ {
+ rb->root = x;
+ }
+
+ rb_insert_fixup(rb, x);
+ return NULL;
+}
+
+/**********************************************************************
+ * Deletion *
+ **********************************************************************/
+
+/*
+ * Maintain Red-Black tree balance after deleting a black node.
+ */
+static void
+rb_delete_fixup(RBTree *rb, RBNode *x)
+{
+ /*
+ * x is always a black node. Initially, it is the former child of the
+ * deleted node. Each iteration of this loop moves it higher up in the
+ * tree.
+ */
+ while (x != rb->root && x->color == BLACK)
+ {
+ /*
+ * Left and right cases are symmetric. Any nodes that are children
+ * of x have a black-height one less than the remainder of the nodes
+ * in the tree. We rotate and recolor nodes to move the problem up
+ * the tree: at some stage we'll either fix the problem, or reach the
+ * root (where the black-height is allowed to decrease).
+ */
+ if (x == x->parent->left)
+ {
+ RBNode *w = x->parent->right;
+
+ if (w->color == RED)
+ {
+ w->color = BLACK;
+ x->parent->color = RED;
+ rb_rotate_left(rb, x->parent);
+ w = x->parent->right;
+ }
+
+ if (w->left->color == BLACK && w->right->color == BLACK)
+ {
+ w->color = RED;
+ x = x->parent;
+ }
+ else
+ {
+ if (w->right->color == BLACK)
+ {
+ w->left->color = BLACK;
+ w->color = RED;
+ rb_rotate_right(rb, w);
+ w = x->parent->right;
+ }
+ w->color = x->parent->color;
+ x->parent->color = BLACK;
+ w->right->color = BLACK;
+ rb_rotate_left(rb, x->parent);
+ x = rb->root; /* Arrange for loop to terminate. */
+ }
+ }
+ else
+ {
+ RBNode *w = x->parent->left;
+
+ if (w->color == RED)
+ {
+ w->color = BLACK;
+ x->parent->color = RED;
+ rb_rotate_right(rb, x->parent);
+ w = x->parent->left;
+ }
+
+ if (w->right->color == BLACK && w->left->color == BLACK)
+ {
+ w->color = RED;
+ x = x->parent;
+ }
+ else
+ {
+ if (w->left->color == BLACK)
+ {
+ w->right->color = BLACK;
+ w->color = RED;
+ rb_rotate_left(rb, w);
+ w = x->parent->left;
+ }
+ w->color = x->parent->color;
+ x->parent->color = BLACK;
+ w->left->color = BLACK;
+ rb_rotate_right(rb, x->parent);
+ x = rb->root; /* Arrange for loop to terminate. */
+ }
+ }
+ }
+ x->color = BLACK;
+}
+
+/*
+ * Delete node z from tree.
+ */
+static void
+rb_delete_node(RBTree *rb, RBNode *z)
+{
+ RBNode *x,
+ *y;
+
+ if (!z || z == RBNIL)
+ return;
+
+ /*
+ * y is the node that will actually be removed from the tree. This will
+ * be z if z has fewer than two children, or the tree successor of z
+ * otherwise.
+ */
+ if (z->left == RBNIL || z->right == RBNIL)
+ {
+ /* y has a RBNIL node as a child */
+ y = z;
+ }
+ else
+ {
+ /* find tree successor */
+ y = z->right;
+ while (y->left != RBNIL)
+ y = y->left;
+ }
+
+ /* x is y's only child */
+ if (y->left != RBNIL)
+ x = y->left;
+ else
+ x = y->right;
+
+ /* Remove y from the tree. */
+ x->parent = y->parent;
+ if (y->parent)
+ {
+ if (y == y->parent->left)
+ y->parent->left = x;
+ else
+ y->parent->right = x;
+ }
+ else
+ {
+ rb->root = x;
+ }
+
+ /*
+ * If we removed the tree successor of z rather than z itself, then
+ * attach the data for the removed node to the one we were supposed to
+ * remove.
+ */
+ if (y != z)
+ z->data = y->data;
+
+ /*
+ * Removing a black node might make some paths from root to leaf contain
+ * fewer black nodes than others, or it might make two red nodes adjacent.
+ */
+ if (y->color == BLACK)
+ rb_delete_fixup(rb, x);
+
+ pfree(y);
+}
+
+extern void
+rb_delete(RBTree *rb, void *data)
+{
+ RBNode *node = rb->root;
+ int cmp;
+
+ while (node != RBNIL)
+ {
+ cmp = rb->comparator(data, node->data, rb->arg);
+
+ if (cmp == 0)
+ {
+ /* found node to delete */
+ if (rb->freefunc)
+ rb->freefunc(node->data);
+ node->data = NULL;
+ rb_delete_node(rb, node);
+ return;
+ }
+ else if (cmp < 0)
+ node = node->left;
+ else
+ node = node->right;
+ }
+}
+
+/*
+ * Return data on left most node and delete
+ * that node
+ */
+extern void *
+rb_leftmost(RBTree *rb)
+{
+ RBNode *node = rb->root;
+ RBNode *leftmost = rb->root;
+ void *res = NULL;
+
+ while (node != RBNIL)
+ {
+ leftmost = node;
+ node = node->left;
+ }
+
+ if (leftmost != RBNIL)
+ {
+ res = leftmost->data;
+ leftmost->data = NULL;
+ rb_delete_node(rb, leftmost);
+ }
+
+ return res;
+}
+
+/**********************************************************************
+ * Traverse *
+ **********************************************************************/
+
+static void *
+rb_next_node(RBTreeIterator *iterator, RBNode *node)
+{
+ node->iteratorState = InitialState;
+ iterator->node = node;
+ return iterator->iterate(iterator);
+}
+
+static void *
+rb_left_right_iterator(RBTreeIterator *iterator)
+{
+ RBNode *node = iterator->node;
+
+ switch (node->iteratorState)
+ {
+ case InitialState:
+ if (node->left != RBNIL)
+ {
+ node->iteratorState = FirstStepDone;
+ return rb_next_node(iterator, node->left);
+ }
+ case FirstStepDone:
+ node->iteratorState = SecondStepDone;
+ return node->data;
+ case SecondStepDone:
+ if (node->right != RBNIL)
+ {
+ node->iteratorState = ThirdStepDone;
+ return rb_next_node(iterator, node->right);
+ }
+ case ThirdStepDone:
+ if (node->parent)
+ {
+ iterator->node = node->parent;
+ return iterator->iterate(iterator);
+ }
+ break;
+ default:
+ elog(ERROR, "Unknow node state: %d", node->iteratorState);
+ }
+
+ return NULL;
+}
+
+static void *
+rb_right_left_iterator(RBTreeIterator *iterator)
+{
+ RBNode *node = iterator->node;
+
+ switch (node->iteratorState)
+ {
+ case InitialState:
+ if (node->right != RBNIL)
+ {
+ node->iteratorState = FirstStepDone;
+ return rb_next_node(iterator, node->right);
+ }
+ case FirstStepDone:
+ node->iteratorState = SecondStepDone;
+ return node->data;
+ case SecondStepDone:
+ if (node->left != RBNIL)
+ {
+ node->iteratorState = ThirdStepDone;
+ return rb_next_node(iterator, node->left);
+ }
+ case ThirdStepDone:
+ if (node->parent)
+ {
+ iterator->node = node->parent;
+ return iterator->iterate(iterator);
+ }
+ break;
+ default:
+ elog(ERROR, "Unknow node state: %d", node->iteratorState);
+ }
+
+ return NULL;
+}
+
+static void *
+rb_direct_iterator(RBTreeIterator *iterator)
+{
+ RBNode *node = iterator->node;
+
+ switch (node->iteratorState)
+ {
+ case InitialState:
+ node->iteratorState = FirstStepDone;
+ return node->data;
+ case FirstStepDone:
+ if (node->left != RBNIL)
+ {
+ node->iteratorState = SecondStepDone;
+ return rb_next_node(iterator, node->left);
+ }
+ case SecondStepDone:
+ if (node->right != RBNIL)
+ {
+ node->iteratorState = ThirdStepDone;
+ return rb_next_node(iterator, node->right);
+ }
+ case ThirdStepDone:
+ if (node->parent)
+ {
+ iterator->node = node->parent;
+ return iterator->iterate(iterator);
+ }
+ break;
+ default:
+ elog(ERROR, "Unknow node state: %d", node->iteratorState);
+ }
+
+ return NULL;
+}
+
+static void *
+rb_inverted_iterator(RBTreeIterator *iterator)
+{
+ RBNode *node = iterator->node;
+
+ switch (node->iteratorState)
+ {
+ case InitialState:
+ if (node->left != RBNIL)
+ {
+ node->iteratorState = FirstStepDone;
+ return rb_next_node(iterator, node->left);
+ }
+ case FirstStepDone:
+ if (node->right != RBNIL)
+ {
+ node->iteratorState = SecondStepDone;
+ return rb_next_node(iterator, node->right);
+ }
+ case SecondStepDone:
+ node->iteratorState = ThirdStepDone;
+ return node->data;
+ case ThirdStepDone:
+ if (node->parent)
+ {
+ iterator->node = node->parent;
+ return iterator->iterate(iterator);
+ }
+ break;
+ default:
+ elog(ERROR, "Unknow node state: %d", node->iteratorState);
+ }
+
+ return NULL;
+}
+
+RBTreeIterator *
+rb_begin_iterate(RBTree *rb, RBOrderControl ctrl)
+{
+ RBTreeIterator *iterator = palloc(sizeof(RBTreeIterator));
+
+ iterator->node = rb->root;
+ if (iterator->node != RBNIL)
+ iterator->node->iteratorState = InitialState;
+
+ switch (ctrl)
+ {
+ case LeftRightWalk: /* visit left, then self, then right */
+ iterator->iterate = rb_left_right_iterator;
+ break;
+ case RightLeftWalk: /* visit right, then self, then left */
+ iterator->iterate = rb_right_left_iterator;
+ break;
+ case DirectWalk: /* visit self, then left, then right */
+ iterator->iterate = rb_direct_iterator;
+ break;
+ case InvertedWalk: /* visit left, then right, then self */
+ iterator->iterate = rb_inverted_iterator;
+ break;
+ default:
+ elog(ERROR, "Unknown iterator order: %d", ctrl);
+ }
+
+ return iterator;
+}
+
+void *
+rb_iterate(RBTreeIterator *iterator)
+{
+ if (iterator->node == RBNIL)
+ return NULL;
+
+ return iterator->iterate(iterator);
+}
+
+void
+rb_free_iterator(RBTreeIterator *iterator)
+{
+ pfree(iterator);
+}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index cb2ae9a..2a84895 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -1882,6 +1882,11 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_LIST(list_PREPARE);
}
+/*
+ * PREPARE TRANSACTION is missing on purpose. It's intended for transaction
+ * managers, not for manual use in interactive sessions.
+ */
+
/* REASSIGN OWNED BY xxx TO yyy */
else if (pg_strcasecmp(prev_wd, "REASSIGN") == 0)
COMPLETE_WITH_CONST("OWNED");
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index b96ff95..fcc5371 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -13,6 +13,7 @@
#include "access/genam.h"
#include "access/itup.h"
#include "access/xlog.h"
+#include "utils/rbtree.h"
#include "fmgr.h"
@@ -27,14 +28,6 @@
#define GINNProcs 5
/*
- * Max depth allowed in search tree during bulk inserts. This is to keep from
- * degenerating to O(N^2) behavior when the tree is unbalanced due to sorted
- * or nearly-sorted input. (Perhaps it would be better to use a balanced-tree
- * algorithm, but in common cases that would only add useless overhead.)
- */
-#define GIN_MAX_TREE_DEPTH 100
-
-/*
* Page opaque data in a inverted index page.
*
* Note: GIN does not include a page ID word as do the other index types.
@@ -571,26 +564,22 @@ extern Datum ginarrayconsistent(PG_FUNCTION_ARGS);
typedef struct EntryAccumulator
{
OffsetNumber attnum;
+ bool shouldSort;
Datum value;
uint32 length;
uint32 number;
ItemPointerData *list;
- bool shouldSort;
- struct EntryAccumulator *left;
- struct EntryAccumulator *right;
} EntryAccumulator;
typedef struct
{
GinState *ginstate;
- EntryAccumulator *entries;
- uint32 maxdepth;
- EntryAccumulator **stack;
- uint32 stackpos;
long allocatedMemory;
-
uint32 length;
- EntryAccumulator *entryallocator;
+ EntryAccumulator *entryallocator;
+ ItemPointerData *tmpList;
+ RBTree *tree;
+ RBTreeIterator *iterator;
} BuildAccumulator;
extern void ginInitBA(BuildAccumulator *accum);
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 30a5033..70965b8 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201001222
+#define CATALOG_VERSION_NO 201001251
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index c4320e5..5ee7443 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -957,6 +957,10 @@ DATA(insert OID = 723 ( get_bit PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "17
DESCR("get bit");
DATA(insert OID = 724 ( set_bit PGNSP PGUID 12 1 0 0 f f f t f i 3 0 17 "17 23 23" _null_ _null_ _null_ _null_ byteaSetBit _null_ _null_ _null_ ));
DESCR("set bit");
+DATA(insert OID = 749 ( overlay PGNSP PGUID 12 1 0 0 f f f t f i 4 0 17 "17 17 23 23" _null_ _null_ _null_ _null_ byteaoverlay _null_ _null_ _null_ ));
+DESCR("substitute portion of string");
+DATA(insert OID = 752 ( overlay PGNSP PGUID 12 1 0 0 f f f t f i 3 0 17 "17 17 23" _null_ _null_ _null_ _null_ byteaoverlay_no_len _null_ _null_ _null_ ));
+DESCR("substitute portion of string");
DATA(insert OID = 725 ( dist_pl PGNSP PGUID 12 1 0 0 f f f t f i 2 0 701 "600 628" _null_ _null_ _null_ _null_ dist_pl _null_ _null_ _null_ ));
DESCR("distance between point and line");
@@ -1832,9 +1836,9 @@ DESCR("current schema name");
DATA(insert OID = 1403 ( current_schemas PGNSP PGUID 12 1 0 0 f f f t f s 1 0 1003 "16" _null_ _null_ _null_ _null_ current_schemas _null_ _null_ _null_ ));
DESCR("current schema search list");
-DATA(insert OID = 1404 ( overlay PGNSP PGUID 14 1 0 0 f f f t f i 4 0 25 "25 25 23 23" _null_ _null_ _null_ _null_ "select pg_catalog.substring($1, 1, ($3 - 1)) || $2 || pg_catalog.substring($1, ($3 + $4))" _null_ _null_ _null_ ));
+DATA(insert OID = 1404 ( overlay PGNSP PGUID 12 1 0 0 f f f t f i 4 0 25 "25 25 23 23" _null_ _null_ _null_ _null_ textoverlay _null_ _null_ _null_ ));
DESCR("substitute portion of string");
-DATA(insert OID = 1405 ( overlay PGNSP PGUID 14 1 0 0 f f f t f i 3 0 25 "25 25 23" _null_ _null_ _null_ _null_ "select pg_catalog.substring($1, 1, ($3 - 1)) || $2 || pg_catalog.substring($1, ($3 + pg_catalog.char_length($2)))" _null_ _null_ _null_ ));
+DATA(insert OID = 1405 ( overlay PGNSP PGUID 12 1 0 0 f f f t f i 3 0 25 "25 25 23" _null_ _null_ _null_ _null_ textoverlay_no_len _null_ _null_ _null_ ));
DESCR("substitute portion of string");
DATA(insert OID = 1406 ( isvertical PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "600 600" _null_ _null_ _null_ _null_ point_vert _null_ _null_ _null_ ));
@@ -2402,9 +2406,17 @@ DESCR("adjust varbit() to typmod length");
DATA(insert OID = 1698 ( position PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "1560 1560" _null_ _null_ _null_ _null_ bitposition _null_ _null_ _null_ ));
DESCR("return position of sub-bitstring");
-DATA(insert OID = 1699 ( substring PGNSP PGUID 12 1 0 0 f f f t f i 2 0 1560 "1560 23" _null_ _null_ _null_ _null_ bitsubstr_no_len _null_ _null_ _null_ ));
+DATA(insert OID = 1699 ( substring PGNSP PGUID 12 1 0 0 f f f t f i 2 0 1560 "1560 23" _null_ _null_ _null_ _null_ bitsubstr_no_len _null_ _null_ _null_ ));
DESCR("return portion of bitstring");
+DATA(insert OID = 3030 ( overlay PGNSP PGUID 12 1 0 0 f f f t f i 4 0 1560 "1560 1560 23 23" _null_ _null_ _null_ _null_ bitoverlay _null_ _null_ _null_ ));
+DESCR("substitute portion of bitstring");
+DATA(insert OID = 3031 ( overlay PGNSP PGUID 12 1 0 0 f f f t f i 3 0 1560 "1560 1560 23" _null_ _null_ _null_ _null_ bitoverlay_no_len _null_ _null_ _null_ ));
+DESCR("substitute portion of bitstring");
+DATA(insert OID = 3032 ( get_bit PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "1560 23" _null_ _null_ _null_ _null_ bitgetbit _null_ _null_ _null_ ));
+DESCR("get bit");
+DATA(insert OID = 3033 ( set_bit PGNSP PGUID 12 1 0 0 f f f t f i 3 0 1560 "1560 23 23" _null_ _null_ _null_ _null_ bitsetbit _null_ _null_ _null_ ));
+DESCR("set bit");
/* for mac type support */
DATA(insert OID = 436 ( macaddr_in PGNSP PGUID 12 1 0 0 f f f t f i 1 0 829 "2275" _null_ _null_ _null_ _null_ macaddr_in _null_ _null_ _null_ ));
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 72e9ed0..9ad9573 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -698,6 +698,8 @@ extern Datum textoctetlen(PG_FUNCTION_ARGS);
extern Datum textpos(PG_FUNCTION_ARGS);
extern Datum text_substr(PG_FUNCTION_ARGS);
extern Datum text_substr_no_len(PG_FUNCTION_ARGS);
+extern Datum textoverlay(PG_FUNCTION_ARGS);
+extern Datum textoverlay_no_len(PG_FUNCTION_ARGS);
extern Datum name_text(PG_FUNCTION_ARGS);
extern Datum text_name(PG_FUNCTION_ARGS);
extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2);
diff --git a/src/include/utils/bytea.h b/src/include/utils/bytea.h
index 6ad3558..b51ea4c 100644
--- a/src/include/utils/bytea.h
+++ b/src/include/utils/bytea.h
@@ -46,5 +46,7 @@ extern Datum byteacat(PG_FUNCTION_ARGS);
extern Datum byteapos(PG_FUNCTION_ARGS);
extern Datum bytea_substr(PG_FUNCTION_ARGS);
extern Datum bytea_substr_no_len(PG_FUNCTION_ARGS);
+extern Datum byteaoverlay(PG_FUNCTION_ARGS);
+extern Datum byteaoverlay_no_len(PG_FUNCTION_ARGS);
#endif /* BYTEA_H */
diff --git a/src/include/utils/rbtree.h b/src/include/utils/rbtree.h
new file mode 100644
index 0000000..6ce74b0
--- /dev/null
+++ b/src/include/utils/rbtree.h
@@ -0,0 +1,47 @@
+/*-------------------------------------------------------------------------
+ *
+ * rbtree.h
+ * interface for PostgreSQL generic Red-Black binary tree package
+ *
+ * Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * $PostgreSQL: pgsql/src/backend/nodes/list.c,v 1.69 2008/01/01 19:45:50 momjian Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef RBTREE_H
+#define RBTREE_H
+
+typedef struct RBTree RBTree;
+typedef struct RBTreeIterator RBTreeIterator;
+
+typedef int (*rb_comparator) (const void *a, const void *b, void *arg);
+typedef void* (*rb_appendator) (void *current, void *new, void *arg);
+typedef void (*rb_freefunc) (void *a);
+
+extern RBTree *rb_create(rb_comparator comparator,
+ rb_appendator appendator,
+ rb_freefunc freefunc,
+ void *arg);
+extern void rb_free(RBTree *rb);
+
+extern void *rb_find(RBTree *rb, void *data);
+extern void *rb_insert(RBTree *rb, void *data);
+extern void rb_delete(RBTree *rb, void *data);
+extern void *rb_leftmost(RBTree *rb);
+
+typedef enum RBOrderControl
+{
+ LeftRightWalk,
+ RightLeftWalk,
+ DirectWalk,
+ InvertedWalk
+} RBOrderControl;
+
+extern RBTreeIterator* rb_begin_iterate(RBTree *rb, RBOrderControl ctrl);
+extern void *rb_iterate(RBTreeIterator *iterator);
+extern void rb_free_iterator(RBTreeIterator *iterator);
+
+#endif
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 57efc34..c2f4005 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -89,6 +89,8 @@ extern Datum bitshiftright(PG_FUNCTION_ARGS);
extern Datum bitcat(PG_FUNCTION_ARGS);
extern Datum bitsubstr(PG_FUNCTION_ARGS);
extern Datum bitsubstr_no_len(PG_FUNCTION_ARGS);
+extern Datum bitoverlay(PG_FUNCTION_ARGS);
+extern Datum bitoverlay_no_len(PG_FUNCTION_ARGS);
extern Datum bitlength(PG_FUNCTION_ARGS);
extern Datum bitoctetlength(PG_FUNCTION_ARGS);
extern Datum bitfromint4(PG_FUNCTION_ARGS);
@@ -96,5 +98,7 @@ extern Datum bittoint4(PG_FUNCTION_ARGS);
extern Datum bitfromint8(PG_FUNCTION_ARGS);
extern Datum bittoint8(PG_FUNCTION_ARGS);
extern Datum bitposition(PG_FUNCTION_ARGS);
+extern Datum bitsetbit(PG_FUNCTION_ARGS);
+extern Datum bitgetbit(PG_FUNCTION_ARGS);
#endif
diff --git a/src/pl/tcl/expected/pltcl_setup.out b/src/pl/tcl/expected/pltcl_setup.out
index 496cf22..e46c1c3 100644
--- a/src/pl/tcl/expected/pltcl_setup.out
+++ b/src/pl/tcl/expected/pltcl_setup.out
@@ -495,3 +495,23 @@ CREATE OPERATOR CLASS tcl_int4_ops
OPERATOR 4 @>=,
OPERATOR 5 @>,
FUNCTION 1 tcl_int4cmp(int4,int4) ;
+--
+-- Test usage of Tcl's "clock" command. In recent Tcl versions this
+-- command fails without working "unknown" support, so it's a good canary
+-- for initialization problems.
+--
+create function tcl_date_week(int4,int4,int4) returns text as $$
+ return [clock format [clock scan "$2/$3/$1"] -format "%U"]
+$$ language pltcl immutable;
+select tcl_date_week(2010,1,24);
+ tcl_date_week
+---------------
+ 04
+(1 row)
+
+select tcl_date_week(2001,10,24);
+ tcl_date_week
+---------------
+ 42
+(1 row)
+
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 2603028..350d471 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -304,9 +304,12 @@ _PG_init(void)
************************************************************/
if ((pltcl_hold_interp = Tcl_CreateInterp()) == NULL)
elog(ERROR, "could not create \"hold\" interpreter");
+ if (Tcl_Init(pltcl_hold_interp) == TCL_ERROR)
+ elog(ERROR, "could not initialize \"hold\" interpreter");
/************************************************************
- * Create the two interpreters
+ * Create the two slave interpreters. Note: Tcl automatically does
+ * Tcl_Init on the normal slave, and it's not wanted for the safe slave.
************************************************************/
if ((pltcl_norm_interp =
Tcl_CreateSlave(pltcl_hold_interp, "norm", 0)) == NULL)
diff --git a/src/pl/tcl/sql/pltcl_setup.sql b/src/pl/tcl/sql/pltcl_setup.sql
index 55ac7e2..4a581ed 100644
--- a/src/pl/tcl/sql/pltcl_setup.sql
+++ b/src/pl/tcl/sql/pltcl_setup.sql
@@ -542,3 +542,15 @@ CREATE OPERATOR CLASS tcl_int4_ops
OPERATOR 4 @>=,
OPERATOR 5 @>,
FUNCTION 1 tcl_int4cmp(int4,int4) ;
+
+--
+-- Test usage of Tcl's "clock" command. In recent Tcl versions this
+-- command fails without working "unknown" support, so it's a good canary
+-- for initialization problems.
+--
+create function tcl_date_week(int4,int4,int4) returns text as $$
+ return [clock format [clock scan "$2/$3/$1"] -format "%U"]
+$$ language pltcl immutable;
+
+select tcl_date_week(2010,1,24);
+select tcl_date_week(2001,10,24);
diff --git a/src/test/regress/expected/bit.out b/src/test/regress/expected/bit.out
index 3563d23..40082ca 100644
--- a/src/test/regress/expected/bit.out
+++ b/src/test/regress/expected/bit.out
@@ -509,3 +509,43 @@ SELECT POSITION(B'1101' IN v),
DROP TABLE BIT_SHIFT_TABLE;
DROP TABLE VARBIT_SHIFT_TABLE;
+-- Get/Set bit
+SELECT get_bit(B'0101011000100', 10);
+ get_bit
+---------
+ 1
+(1 row)
+
+SELECT set_bit(B'0101011000100100', 15, 1);
+ set_bit
+------------------
+ 0101011000100101
+(1 row)
+
+SELECT set_bit(B'0101011000100100', 16, 1); -- fail
+ERROR: bit index 16 out of valid range (0..15)
+-- Overlay
+SELECT overlay(B'0101011100' placing '001' from 2 for 3);
+ overlay
+------------
+ 0001011100
+(1 row)
+
+SELECT overlay(B'0101011100' placing '101' from 6);
+ overlay
+------------
+ 0101010100
+(1 row)
+
+SELECT overlay(B'0101011100' placing '001' from 11);
+ overlay
+---------------
+ 0101011100001
+(1 row)
+
+SELECT overlay(B'0101011100' placing '001' from 20);
+ overlay
+---------------
+ 0101011100001
+(1 row)
+
diff --git a/src/test/regress/expected/strings.out b/src/test/regress/expected/strings.out
index 392f48e..42c34a5 100644
--- a/src/test/regress/expected/strings.out
+++ b/src/test/regress/expected/strings.out
@@ -1579,3 +1579,21 @@ SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea);
\000trim\000
(1 row)
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape');
+ encode
+-------------
+ TTh\x01omas
+(1 row)
+
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape');
+ encode
+--------------------
+ Th\000omas\x02\x03
+(1 row)
+
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape');
+ encode
+-----------------
+ Th\000o\x02\x03
+(1 row)
+
diff --git a/src/test/regress/sql/bit.sql b/src/test/regress/sql/bit.sql
index f178991..73ddd37 100644
--- a/src/test/regress/sql/bit.sql
+++ b/src/test/regress/sql/bit.sql
@@ -184,3 +184,14 @@ SELECT POSITION(B'1101' IN v),
DROP TABLE BIT_SHIFT_TABLE;
DROP TABLE VARBIT_SHIFT_TABLE;
+
+-- Get/Set bit
+SELECT get_bit(B'0101011000100', 10);
+SELECT set_bit(B'0101011000100100', 15, 1);
+SELECT set_bit(B'0101011000100100', 16, 1); -- fail
+
+-- Overlay
+SELECT overlay(B'0101011100' placing '001' from 2 for 3);
+SELECT overlay(B'0101011100' placing '101' from 6);
+SELECT overlay(B'0101011100' placing '001' from 11);
+SELECT overlay(B'0101011100' placing '001' from 20);
diff --git a/src/test/regress/sql/strings.sql b/src/test/regress/sql/strings.sql
index 63df940..8c35be8 100644
--- a/src/test/regress/sql/strings.sql
+++ b/src/test/regress/sql/strings.sql
@@ -547,3 +547,6 @@ SELECT trim(E'\\000'::bytea from E'\\000Tom\\000'::bytea);
SELECT btrim(E'\\000trim\\000'::bytea, E'\\000'::bytea);
SELECT btrim(''::bytea, E'\\000'::bytea);
SELECT btrim(E'\\000trim\\000'::bytea, ''::bytea);
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'Th\\001omas'::bytea from 2),'escape');
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 8),'escape');
+SELECT encode(overlay(E'Th\\000omas'::bytea placing E'\\002\\003'::bytea from 5 for 3),'escape');