1 /*-------------------------------------------------------------------------
2  *
3  * listutils.c
4  *
5  * This file contains functions to perform useful operations on lists.
6  *
7  * Copyright (c) Citus Data, Inc.
8  *
9  *-------------------------------------------------------------------------
10  */
11 
12 #include "postgres.h"
13 #include "c.h"
14 #include "port.h"
15 
16 #include "utils/lsyscache.h"
17 #include "lib/stringinfo.h"
18 #include "distributed/citus_safe_lib.h"
19 #include "distributed/listutils.h"
20 #include "nodes/pg_list.h"
21 #include "utils/memutils.h"
22 
23 
24 /*
25  * SortList takes in a list of void pointers, and sorts these pointers (and the
26  * values they point to) by applying the given comparison function. The function
27  * then returns the sorted list of pointers.
28  *
29  * Because the input list is a list of pointers, and because qsort expects to
30  * compare pointers to the list elements, the provided comparison function must
31  * compare pointers to pointers to elements. In addition, this sort function
32  * naturally exhibits the same lack of stability exhibited by qsort. See that
33  * function's man page for more details.
34  */
35 List *
SortList(List * pointerList,int (* comparisonFunction)(const void *,const void *))36 SortList(List *pointerList, int (*comparisonFunction)(const void *, const void *))
37 {
38 	List *sortedList = NIL;
39 	uint32 arrayIndex = 0;
40 	uint32 arraySize = (uint32) list_length(pointerList);
41 	void **array = (void **) palloc0(arraySize * sizeof(void *));
42 
43 	void *pointer = NULL;
44 	foreach_ptr(pointer, pointerList)
45 	{
46 		array[arrayIndex] = pointer;
47 
48 		arrayIndex++;
49 	}
50 
51 	/* sort the array of pointers using the comparison function */
52 	SafeQsort(array, arraySize, sizeof(void *), comparisonFunction);
53 
54 	/* convert the sorted array of pointers back to a sorted list */
55 	for (arrayIndex = 0; arrayIndex < arraySize; arrayIndex++)
56 	{
57 		void *sortedPointer = array[arrayIndex];
58 		sortedList = lappend(sortedList, sortedPointer);
59 	}
60 
61 	pfree(array);
62 
63 	if (sortedList != NIL)
64 	{
65 		sortedList->type = pointerList->type;
66 	}
67 
68 	return sortedList;
69 }
70 
71 
72 /*
73  * PointerArrayFromList converts a list of pointers to an array of pointers.
74  */
75 void **
PointerArrayFromList(List * pointerList)76 PointerArrayFromList(List *pointerList)
77 {
78 	int pointerCount = list_length(pointerList);
79 	void **pointerArray = (void **) palloc0(pointerCount * sizeof(void *));
80 	int pointerIndex = 0;
81 
82 	void *pointer = NULL;
83 	foreach_ptr(pointer, pointerList)
84 	{
85 		pointerArray[pointerIndex] = pointer;
86 		pointerIndex += 1;
87 	}
88 
89 	return pointerArray;
90 }
91 
92 
93 /*
94  * DatumArrayToArrayType converts the provided Datum array (of the specified
95  * length and type) into an ArrayType suitable for returning from a UDF.
96  */
97 ArrayType *
DatumArrayToArrayType(Datum * datumArray,int datumCount,Oid datumTypeId)98 DatumArrayToArrayType(Datum *datumArray, int datumCount, Oid datumTypeId)
99 {
100 	int16 typeLength = 0;
101 	bool typeByValue = false;
102 	char typeAlignment = 0;
103 
104 	get_typlenbyvalalign(datumTypeId, &typeLength, &typeByValue, &typeAlignment);
105 	ArrayType *arrayObject = construct_array(datumArray, datumCount, datumTypeId,
106 											 typeLength, typeByValue, typeAlignment);
107 
108 	return arrayObject;
109 }
110 
111 
112 /*
113  * ListToHashSet creates a hash table in which the keys are copied from
114  * from itemList and the values are the same as the keys. This can
115  * be used for fast lookups of the presence of a byte array in a set.
116  * itemList should be a list of pointers.
117  *
118  * If isStringList is true, then look-ups are performed through string
119  * comparison of strings up to keySize in length. If isStringList is
120  * false, then look-ups are performed by comparing exactly keySize
121  * bytes pointed to by the pointers in itemList.
122  */
123 HTAB *
ListToHashSet(List * itemList,Size keySize,bool isStringList)124 ListToHashSet(List *itemList, Size keySize, bool isStringList)
125 {
126 	HASHCTL info;
127 	int flags = HASH_ELEM | HASH_CONTEXT;
128 
129 	/* allocate sufficient capacity for O(1) expected look-up time */
130 	int capacity = (int) (list_length(itemList) / 0.75) + 1;
131 
132 	/* initialise the hash table for looking up keySize bytes */
133 	memset(&info, 0, sizeof(info));
134 	info.keysize = keySize;
135 	info.entrysize = keySize;
136 	info.hcxt = CurrentMemoryContext;
137 
138 	if (isStringList)
139 	{
140 #if PG_VERSION_NUM >= PG_VERSION_14
141 		flags |= HASH_STRINGS;
142 #endif
143 	}
144 	else
145 	{
146 		flags |= HASH_BLOBS;
147 	}
148 
149 	HTAB *itemSet = hash_create("ListToHashSet", capacity, &info, flags);
150 
151 	void *item = NULL;
152 	foreach_ptr(item, itemList)
153 	{
154 		bool foundInSet = false;
155 
156 		hash_search(itemSet, item, HASH_ENTER, &foundInSet);
157 	}
158 
159 	return itemSet;
160 }
161 
162 
163 /*
164  * GeneratePositiveIntSequenceList generates a positive int
165  * sequence list up to the given number. The list will have:
166  * [1:upto]
167  */
168 List *
GeneratePositiveIntSequenceList(int upTo)169 GeneratePositiveIntSequenceList(int upTo)
170 {
171 	List *intList = NIL;
172 	for (int i = 1; i <= upTo; i++)
173 	{
174 		intList = lappend_int(intList, i);
175 	}
176 	return intList;
177 }
178 
179 
180 /*
181  * StringJoin gets a list of char * and then simply
182  * returns a newly allocated char * joined with the
183  * given delimiter.
184  */
185 char *
StringJoin(List * stringList,char delimiter)186 StringJoin(List *stringList, char delimiter)
187 {
188 	StringInfo joinedString = makeStringInfo();
189 
190 	const char *command = NULL;
191 	int curIndex = 0;
192 	foreach_ptr(command, stringList)
193 	{
194 		if (curIndex > 0)
195 		{
196 			appendStringInfoChar(joinedString, delimiter);
197 		}
198 		appendStringInfoString(joinedString, command);
199 		curIndex++;
200 	}
201 
202 	return joinedString->data;
203 }
204 
205 
206 /*
207  * ListTake returns the first size elements of given list. If size is greater
208  * than list's length, it returns all elements of list. This is modeled after
209  * the "take" function used in some Scheme implementations.
210  */
211 List *
ListTake(List * pointerList,int size)212 ListTake(List *pointerList, int size)
213 {
214 	List *result = NIL;
215 	int listIndex = 0;
216 
217 	void *pointer = NULL;
218 	foreach_ptr(pointer, pointerList)
219 	{
220 		result = lappend(result, pointer);
221 		listIndex++;
222 		if (listIndex >= size)
223 		{
224 			break;
225 		}
226 	}
227 
228 	return result;
229 }
230 
231 
232 /*
233  * safe_list_nth first checks if given index is valid and errors out if it is
234  * not. Otherwise, it directly calls list_nth.
235  */
236 void *
safe_list_nth(const List * list,int index)237 safe_list_nth(const List *list, int index)
238 {
239 	int listLength = list_length(list);
240 	if (index < 0 || index >= listLength)
241 	{
242 		ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
243 						errmsg("invalid list access: list length was %d but "
244 							   "element at index %d was requested ",
245 							   listLength, index)));
246 	}
247 
248 	return list_nth(list, index);
249 }
250 
251 
252 /*
253  * GenerateListFromElement returns a new list with length of listLength
254  * such that all the elements are identical with input listElement pointer.
255  */
256 List *
GenerateListFromElement(void * listElement,int listLength)257 GenerateListFromElement(void *listElement, int listLength)
258 {
259 	List *list = NIL;
260 	for (int i = 0; i < listLength; i++)
261 	{
262 		list = lappend(list, listElement);
263 	}
264 
265 	return list;
266 }
267