[ASTERIXDB-3615][FUN]: lpad and rpad func

- user model changes: no
- storage format changes: no
- interface changes: no

Details:
- Implements the LPAD(input_str, length, pad_char) function
for left-padding strings to a target length using the specified pad_str.
- Truncates input if longer than the target length
- Supports Unicode input_str and pad_str.
- Implements the RPAD(input_str, length, pad_char) function
for right-padding strings to a target codepoint length using the pad_str.

Ext-ref: MB-67008

Change-Id: Ia74279fbbe9d23538c057cbbae9538cb219da1f2
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/19828
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.00.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.00.ddl.sqlpp
new file mode 100644
index 0000000..897aac5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.00.ddl.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+drop dataset padding_example if exists;
+create dataset padding_example primary key (id:int);
+upsert into padding_example ([
+{"id":1, "f":"Hi", "f1": "👩"},
+{"id":2, "f": "-7262.98", "f1": "👨‍"},
+{"id":3, "f": "a𩸽b", "f1": "🎉"},
+{"id":4, "f": "👩‍👩‍👧‍👦", "f1": "✓✓✓"},
+{"id":5, "f": null, "f1" : null}
+]);
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.01.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.01.query.sqlpp
new file mode 100644
index 0000000..9b3f0bf
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.01.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+SELECT id, f, f1,
+    lpad(f, 10, '$') AS f_padded_with_dollar_to_10_codepoints,
+    lpad(f, 2, '$') AS f_truncated_or_padded_to_2_codepoints,
+    lpad(f, 10, '👩‍👩‍👧‍👦') AS f_padded_with_emoji_to_10_codepoints,
+    lpad(f1, 4, '%%') AS f1_padded_to_4_codepoints
+FROM padding_example
+ORDER BY id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.02.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.02.query.sqlpp
new file mode 100644
index 0000000..a1383e6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.02.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+SELECT
+    lpad('asterix', 11, '$&^') AS padded_to_11_codepoints,
+    lpad('👩‍👩‍👧‍', 2, '$') AS truncated_to_2_codepoints,
+    lpad('👩‍👩‍👧‍', 10, '👩‍👩‍👧‍👦') AS padded_to_10_codepoints,
+    lpad(null, 4, '%%') AS padded_to_null_is_null,
+    lpad(22, 11, 'y') AS padded_non_str,
+    lpad('abcd', -2, 'y') AS negative_length;
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.03.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.03.query.sqlpp
new file mode 100644
index 0000000..5adab87
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/lpad/lpad.03.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+SELECT lpad(repeat('a', 228),  240, '$') AS padded_long_input,
+       lpad(repeat('u', 310) || '你8世界🌍', 1000, repeat('彩2', 150) || 'END') AS very_long_unicode_test,
+       lpad(repeat('s', 95) || '©®™§¶你好世界🌍', 100, '$') AS very_long_truncation_with_unicode_result;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.00.ddl.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.00.ddl.sqlpp
new file mode 100644
index 0000000..897aac5
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.00.ddl.sqlpp
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+drop dataset padding_example if exists;
+create dataset padding_example primary key (id:int);
+upsert into padding_example ([
+{"id":1, "f":"Hi", "f1": "👩"},
+{"id":2, "f": "-7262.98", "f1": "👨‍"},
+{"id":3, "f": "a𩸽b", "f1": "🎉"},
+{"id":4, "f": "👩‍👩‍👧‍👦", "f1": "✓✓✓"},
+{"id":5, "f": null, "f1" : null}
+]);
+
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.01.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.01.query.sqlpp
new file mode 100644
index 0000000..a95ec0b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.01.query.sqlpp
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+
+SELECT id, f, f1,
+    rpad(f, 10, '$') AS f_padded_with_dollar_to_10_codepoints,
+    rpad(f, 2, '$') AS f_truncated_or_padded_to_2_codepoints,
+    rpad(f, 10, '👩‍👩‍👧‍👦') AS f_padded_with_emoji_to_10_codepoints,
+    rpad(f1, 4, '%%') AS f1_padded_to_4_codepoints
+FROM padding_example
+ORDER BY id;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.02.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.02.query.sqlpp
new file mode 100644
index 0000000..c0d1316
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.02.query.sqlpp
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+SELECT
+    rpad('asterix', 11, '$&^') AS padded_to_11_codepoints,
+    rpad('👩‍👩‍👧‍', 2, '$') AS truncated_to_2_codepoints,
+    rpad('👩‍👩‍👧‍', 10, '👩‍👩‍👧‍👦') AS padded_to_10_codepoints,
+    rpad(null, 4, '%%') AS padded_to_null_is_null,
+    rpad(22, 11, 'y') AS padded_non_str;
+
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.03.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.03.query.sqlpp
new file mode 100644
index 0000000..701d36b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/string/pad/rpad/rpad.03.query.sqlpp
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+SELECT rpad(repeat('a', 228),  240, '$') AS padded_long_input,
+       rpad(repeat('u', 310) || '你8世界🌍', 1000, repeat('彩2', 150) || 'END') AS very_long_unicode_test,
+       rpad(repeat('s', 95) || '©®™§¶你好世界🌍', 100, '$') AS very_long_truncation_with_unicode_result;
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.01.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.01.adm
new file mode 100644
index 0000000..006e70c
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.01.adm
@@ -0,0 +1,5 @@
+{ "id": 1, "f_padded_with_dollar_to_10_codepoints": "$$$$$$$$Hi", "f_truncated_or_padded_to_2_codepoints": "Hi", "f_padded_with_emoji_to_10_codepoints": "👩‍👩‍👧‍👦👩Hi", "f1_padded_to_4_codepoints": "%%%👩", "f": "Hi", "f1": "👩" }
+{ "id": 2, "f_padded_with_dollar_to_10_codepoints": "$$-7262.98", "f_truncated_or_padded_to_2_codepoints": "-7", "f_padded_with_emoji_to_10_codepoints": "👩‍-7262.98", "f1_padded_to_4_codepoints": "%%👨‍", "f": "-7262.98", "f1": "👨‍" }
+{ "id": 3, "f_padded_with_dollar_to_10_codepoints": "$$$$$$$a𩸽b", "f_truncated_or_padded_to_2_codepoints": "a𩸽", "f_padded_with_emoji_to_10_codepoints": "👩‍👩‍👧‍👦a𩸽b", "f1_padded_to_4_codepoints": "%%%🎉", "f": "a𩸽b", "f1": "🎉" }
+{ "id": 4, "f_padded_with_dollar_to_10_codepoints": "$$$👩‍👩‍👧‍👦", "f_truncated_or_padded_to_2_codepoints": "👩‍", "f_padded_with_emoji_to_10_codepoints": "👩‍👩👩‍👩‍👧‍👦", "f1_padded_to_4_codepoints": "%✓✓✓", "f": "👩‍👩‍👧‍👦", "f1": "✓✓✓" }
+{ "id": 5, "f_padded_with_dollar_to_10_codepoints": null, "f_truncated_or_padded_to_2_codepoints": null, "f_padded_with_emoji_to_10_codepoints": null, "f1_padded_to_4_codepoints": null, "f": null, "f1": null }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.02.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.02.adm
new file mode 100644
index 0000000..3f6d3e4
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.02.adm
@@ -0,0 +1 @@
+{ "padded_to_11_codepoints": "$&^$asterix", "truncated_to_2_codepoints": "👩‍", "padded_to_10_codepoints": "👩‍👩‍👩‍👩‍👧‍", "padded_to_null_is_null": null, "padded_non_str": null, "negative_length": null }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.03.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.03.adm
new file mode 100644
index 0000000..6ef482b
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/lpad/lpad.03.adm
@@ -0,0 +1 @@
+{ "padded_long_input": "$$$$$$$$$$$$aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "very_long_unicode_test": "彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2END彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2END彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu你8世界🌍", "very_long_truncation_with_unicode_result": "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss©®™§¶" }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.01.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.01.adm
new file mode 100644
index 0000000..d534124
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.01.adm
@@ -0,0 +1,5 @@
+{ "id": 1, "f_padded_with_dollar_to_10_codepoints": "Hi$$$$$$$$", "f_truncated_or_padded_to_2_codepoints": "Hi", "f_padded_with_emoji_to_10_codepoints": "Hi👩‍👩‍👧‍👦👩", "f1_padded_to_4_codepoints": "👩%%%", "f": "Hi", "f1": "👩" }
+{ "id": 2, "f_padded_with_dollar_to_10_codepoints": "-7262.98$$", "f_truncated_or_padded_to_2_codepoints": "-7", "f_padded_with_emoji_to_10_codepoints": "-7262.98👩‍", "f1_padded_to_4_codepoints": "👨‍%%", "f": "-7262.98", "f1": "👨‍" }
+{ "id": 3, "f_padded_with_dollar_to_10_codepoints": "a𩸽b$$$$$$$", "f_truncated_or_padded_to_2_codepoints": "a𩸽", "f_padded_with_emoji_to_10_codepoints": "a𩸽b👩‍👩‍👧‍👦", "f1_padded_to_4_codepoints": "🎉%%%", "f": "a𩸽b", "f1": "🎉" }
+{ "id": 4, "f_padded_with_dollar_to_10_codepoints": "👩‍👩‍👧‍👦$$$", "f_truncated_or_padded_to_2_codepoints": "👩‍", "f_padded_with_emoji_to_10_codepoints": "👩‍👩‍👧‍👦👩‍👩", "f1_padded_to_4_codepoints": "✓✓✓%", "f": "👩‍👩‍👧‍👦", "f1": "✓✓✓" }
+{ "id": 5, "f_padded_with_dollar_to_10_codepoints": null, "f_truncated_or_padded_to_2_codepoints": null, "f_padded_with_emoji_to_10_codepoints": null, "f1_padded_to_4_codepoints": null, "f": null, "f1": null }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.02.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.02.adm
new file mode 100644
index 0000000..ba875f6
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.02.adm
@@ -0,0 +1 @@
+{ "padded_to_11_codepoints": "asterix$&^$", "truncated_to_2_codepoints": "👩‍", "padded_to_10_codepoints": "👩‍👩‍👧‍👩‍👩‍", "padded_to_null_is_null": null, "padded_non_str": null }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.03.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.03.adm
new file mode 100644
index 0000000..8f86a8a
--- /dev/null
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/string/pad/rpad/rpad.03.adm
@@ -0,0 +1 @@
+{ "padded_long_input": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$$$$$$$$$$$$", "very_long_unicode_test": "uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu你8世界🌍彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2END彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2END彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩2彩", "very_long_truncation_with_unicode_result": "sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss©®™§¶" }
\ No newline at end of file
diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
index de55877..600823a 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/sqlpp_queries.xml
@@ -11711,6 +11711,16 @@
         <expected-warn>Function 'position1' failed to evaluate because: Decoding error - got a low surrogate without a leading high surrogate</expected-warn>
       </compilation-unit>
     </test-case>
+    <test-case FilePath="string">
+      <compilation-unit name="pad/lpad">
+        <output-dir compare="Text">pad/lpad</output-dir>
+      </compilation-unit>
+    </test-case>
+    <test-case FilePath="string">
+      <compilation-unit name="pad/rpad">
+        <output-dir compare="Text">pad/rpad</output-dir>
+      </compilation-unit>
+    </test-case>
   </test-group>
   <test-group name="subquery">
     <test-case FilePath="subquery">
diff --git a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
index 790f040..f631b32 100644
--- a/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
+++ b/asterixdb/asterix-om/src/main/java/org/apache/asterix/om/functions/BuiltinFunctions.java
@@ -398,6 +398,8 @@
     public static final FunctionIdentifier STRING_REPEAT = FunctionConstants.newAsterix("repeat", 2);
     public static final FunctionIdentifier STRING_SPLIT = FunctionConstants.newAsterix("split", 2);
     public static final FunctionIdentifier STRING_PARSE_JSON = FunctionConstants.newAsterix("parse-json", 1);
+    public static final FunctionIdentifier STRING_LPAD = FunctionConstants.newAsterix("lpad", 3);
+    public static final FunctionIdentifier STRING_RPAD = FunctionConstants.newAsterix("rpad", 3);
 
     public static final FunctionIdentifier DATASET =
             FunctionConstants.newAsterix("dataset", FunctionIdentifier.VARARGS); // 1, 2 or 3
@@ -1343,7 +1345,6 @@
         addFunction(NOT, ABooleanTypeComputer.INSTANCE, true);
 
         addFunction(GET_TYPE, AStringTypeComputer.INSTANCE, true);
-
         addPrivateFunction(EQ, BooleanFunctionTypeComputer.INSTANCE, true);
         addPrivateFunction(LE, BooleanFunctionTypeComputer.INSTANCE, true);
         addPrivateFunction(GE, BooleanFunctionTypeComputer.INSTANCE, true);
@@ -1540,6 +1541,8 @@
         addFunction(STRING_REPEAT, AStringTypeComputer.INSTANCE_NULLABLE, true);
         addFunction(STRING_SPLIT, UniformInputTypeComputer.STRING_STRING_LIST_INSTANCE, true);
         addFunction(STRING_PARSE_JSON, AnyTypeComputer.INSTANCE, true);
+        addFunction(STRING_LPAD, AStringTypeComputer.INSTANCE_NULLABLE, true);
+        addFunction(STRING_RPAD, AStringTypeComputer.INSTANCE_NULLABLE, true);
 
         addPrivateFunction(ORDERED_LIST_CONSTRUCTOR, OrderedListConstructorTypeComputer.INSTANCE, true);
         addFunction(POINT_CONSTRUCTOR, APointTypeComputer.INSTANCE, true);
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringLpadDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringLpadDescriptor.java
new file mode 100644
index 0000000..a801d9a
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringLpadDescriptor.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.evaluators.functions;
+
+import org.apache.asterix.common.annotations.MissingNullInOutFunction;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IEvaluatorContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+@MissingNullInOutFunction
+public class StringLpadDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+    private static final long serialVersionUID = 1L;
+
+    public static final IFunctionDescriptorFactory FACTORY = StringLpadDescriptor::new;
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return BuiltinFunctions.STRING_LPAD;
+    }
+
+    @Override
+    public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) {
+        return new IScalarEvaluatorFactory() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException {
+                return new StringPadEvaluator(ctx, args[0], args[1], args[2], StringLpadDescriptor.this.getIdentifier(),
+                        sourceLoc, true) {
+                };
+            }
+        };
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringPadEvaluator.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringPadEvaluator.java
new file mode 100644
index 0000000..d4b01e2
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringPadEvaluator.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.evaluators.functions;
+
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.asterix.om.base.AMutableInt32;
+import org.apache.asterix.om.exceptions.ExceptionUtil;
+import org.apache.asterix.om.types.ATypeTag;
+import org.apache.asterix.runtime.evaluators.common.ArgumentUtils;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IEvaluatorContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.api.exceptions.SourceLocation;
+import org.apache.hyracks.data.std.api.IPointable;
+import org.apache.hyracks.data.std.primitive.UTF8StringPointable;
+import org.apache.hyracks.data.std.primitive.VoidPointable;
+import org.apache.hyracks.data.std.util.ArrayBackedValueStorage;
+import org.apache.hyracks.data.std.util.GrowableArray;
+import org.apache.hyracks.data.std.util.UTF8StringBuilder;
+import org.apache.hyracks.dataflow.common.data.accessors.IFrameTupleReference;
+import org.apache.hyracks.util.string.UTF8StringUtil;
+
+public abstract class StringPadEvaluator extends AbstractScalarEval {
+
+    private final IEvaluatorContext ctx;
+    private final IScalarEvaluator inStrEval;
+    private final IScalarEvaluator lenEval;
+    private final IScalarEvaluator padStrEval;
+
+    private final UTF8StringPointable inStrUtf8Ptr = new UTF8StringPointable();
+    private final UTF8StringPointable padStrUtf8Ptr = new UTF8StringPointable();
+    private final IPointable inStrArg = new VoidPointable();
+    private final IPointable lenArg = new VoidPointable();
+    private final IPointable padStrArg = new VoidPointable();
+    private final AMutableInt32 mutableInt = new AMutableInt32(0);
+
+    private final ArrayBackedValueStorage resultStorage = new ArrayBackedValueStorage();
+    private final DataOutput out = resultStorage.getDataOutput();
+    private final GrowableArray builderInternalArray = new GrowableArray();
+    private final UTF8StringBuilder builder = new UTF8StringBuilder();
+    private final boolean lpad;
+
+    public StringPadEvaluator(IEvaluatorContext ctx, IScalarEvaluatorFactory inStrEvalFact,
+            IScalarEvaluatorFactory lenEvalFact, IScalarEvaluatorFactory padStrEvalFact, FunctionIdentifier funcID,
+            SourceLocation srcLoc, boolean lpad) throws HyracksDataException {
+        super(srcLoc, funcID);
+        this.lpad = lpad;
+        this.inStrEval = inStrEvalFact.createScalarEvaluator(ctx);
+        this.lenEval = lenEvalFact.createScalarEvaluator(ctx);
+        this.padStrEval = padStrEvalFact.createScalarEvaluator(ctx);
+        this.ctx = ctx;
+    }
+
+    @Override
+    public void evaluate(IFrameTupleReference tuple, IPointable result) throws HyracksDataException {
+        inStrEval.evaluate(tuple, inStrArg);
+        lenEval.evaluate(tuple, lenArg);
+        padStrEval.evaluate(tuple, padStrArg);
+
+        if (PointableHelper.checkAndSetMissingOrNull(result, inStrArg, lenArg, padStrArg)) {
+            return;
+        }
+
+        byte[] inStrBytes = inStrArg.getByteArray();
+        int inStrStart = inStrArg.getStartOffset();
+        if (inStrBytes[inStrStart] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
+            PointableHelper.setNull(result);
+            ExceptionUtil.warnTypeMismatch(ctx, srcLoc, funID, inStrBytes[inStrStart], 0, ATypeTag.STRING);
+            return;
+        }
+
+        byte[] lenBytes = lenArg.getByteArray();
+        int lenStart = lenArg.getStartOffset();
+        if (!ArgumentUtils.setInteger(ctx, srcLoc, funID, 1, lenBytes, lenStart, mutableInt)) {
+            PointableHelper.setNull(result);
+            return;
+        }
+        // Desired length of the string in Unicode code points
+        int targetNumCodePoints = mutableInt.getIntegerValue();
+        if (targetNumCodePoints < 0) {
+            PointableHelper.setNull(result);
+            ExceptionUtil.warnNegativeValue(ctx, srcLoc, funID, 1, targetNumCodePoints);
+            return;
+        }
+
+        // TODO: usually this will be a constant. if constant, the code could be optimized
+        byte[] padStrBytes = padStrArg.getByteArray();
+        int padStrStart = padStrArg.getStartOffset();
+        if (padStrBytes[padStrStart] != ATypeTag.SERIALIZED_STRING_TYPE_TAG) {
+            PointableHelper.setNull(result);
+            ExceptionUtil.warnTypeMismatch(ctx, srcLoc, funID, padStrBytes[padStrStart], 2, ATypeTag.STRING);
+            return;
+        }
+
+        int inStrCodePointsNum = UTF8StringUtil.getNumCodePoint(inStrBytes, inStrStart + 1);
+        if (inStrCodePointsNum == targetNumCodePoints) {
+            result.set(inStrArg);
+            return;
+        }
+        try {
+            builderInternalArray.reset();
+            builder.reset(builderInternalArray, targetNumCodePoints);
+            // skip the type tag byte
+            inStrUtf8Ptr.set(inStrBytes, inStrStart + 1, inStrArg.getLength() - 1);
+            padStrUtf8Ptr.set(padStrBytes, padStrStart + 1, padStrArg.getLength() - 1);
+
+            process(targetNumCodePoints, inStrCodePointsNum);
+            builder.finish();
+
+            resultStorage.reset();
+            out.writeByte(ATypeTag.SERIALIZED_STRING_TYPE_TAG);
+            out.write(builderInternalArray.getByteArray(), 0, builderInternalArray.getLength());
+            //result -> type tag, len, actual data
+            result.set(resultStorage);
+        } catch (IOException e) {
+            throw HyracksDataException.create(e);
+        }
+    }
+
+    public void process(int targetNumCodePoints, int inStrCodePointsNum) throws IOException {
+        if (targetNumCodePoints < inStrCodePointsNum) {
+            truncate(targetNumCodePoints);
+        } else {
+            int numCodePointsToPad = targetNumCodePoints - inStrCodePointsNum;
+            if (lpad) {
+                // For LPAD: first, append the required padding characters.
+                appendPaddingCodepoints(numCodePointsToPad);
+                // Then, append the codepoints of the original string.
+                appendCodePoints();
+            } else {
+                appendCodePoints();
+                appendPaddingCodepoints(numCodePointsToPad);
+            }
+        }
+    }
+
+    private void truncate(int targetNumCodePoints) throws IOException {
+        UTF8StringPointable.append(inStrUtf8Ptr, targetNumCodePoints, builder, builderInternalArray);
+    }
+
+    private void appendCodePoints() throws IOException {
+        builder.appendUtf8StringPointable(inStrUtf8Ptr);
+    }
+
+    private void appendPaddingCodepoints(int numCodePointsToPad) throws IOException {
+        UTF8StringPointable.append(padStrUtf8Ptr, numCodePointsToPad, builder, builderInternalArray);
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRpadDescriptor.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRpadDescriptor.java
new file mode 100644
index 0000000..2ff28e4
--- /dev/null
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/evaluators/functions/StringRpadDescriptor.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.asterix.runtime.evaluators.functions;
+
+import org.apache.asterix.common.annotations.MissingNullInOutFunction;
+import org.apache.asterix.om.functions.BuiltinFunctions;
+import org.apache.asterix.om.functions.IFunctionDescriptorFactory;
+import org.apache.asterix.runtime.evaluators.base.AbstractScalarFunctionDynamicDescriptor;
+import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluator;
+import org.apache.hyracks.algebricks.runtime.base.IScalarEvaluatorFactory;
+import org.apache.hyracks.api.context.IEvaluatorContext;
+import org.apache.hyracks.api.exceptions.HyracksDataException;
+
+@MissingNullInOutFunction
+public class StringRpadDescriptor extends AbstractScalarFunctionDynamicDescriptor {
+    private static final long serialVersionUID = 1L;
+
+    public static final IFunctionDescriptorFactory FACTORY = StringRpadDescriptor::new;
+
+    @Override
+    public FunctionIdentifier getIdentifier() {
+        return BuiltinFunctions.STRING_RPAD;
+    }
+
+    @Override
+    public IScalarEvaluatorFactory createEvaluatorFactory(IScalarEvaluatorFactory[] args) {
+        return new IScalarEvaluatorFactory() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public IScalarEvaluator createScalarEvaluator(IEvaluatorContext ctx) throws HyracksDataException {
+                return new StringPadEvaluator(ctx, args[0], args[1], args[2], StringRpadDescriptor.this.getIdentifier(),
+                        sourceLoc, false) {
+                };
+            }
+        };
+    }
+}
diff --git a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
index b4710de..166352c 100644
--- a/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
+++ b/asterixdb/asterix-runtime/src/main/java/org/apache/asterix/runtime/functions/FunctionCollection.java
@@ -483,6 +483,7 @@
 import org.apache.asterix.runtime.evaluators.functions.StringLengthDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringLikeDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringLowerCaseDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.StringLpadDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringPositionDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringPositionOffset1Descriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringRTrim2Descriptor;
@@ -503,6 +504,7 @@
 import org.apache.asterix.runtime.evaluators.functions.StringReplaceDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringReplaceWithLimitDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringReverseDescriptor;
+import org.apache.asterix.runtime.evaluators.functions.StringRpadDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringSplitDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringStartsWithDescriptor;
 import org.apache.asterix.runtime.evaluators.functions.StringToCodePointDescriptor;
@@ -1146,6 +1148,8 @@
         fc.add(StringReplaceWithLimitDescriptor.FACTORY);
         fc.add(StringReverseDescriptor.FACTORY);
         fc.add(StringSplitDescriptor.FACTORY);
+        fc.add(StringLpadDescriptor.FACTORY);
+        fc.add(StringRpadDescriptor.FACTORY);
 
         // Constructors
         fc.add(ABooleanConstructorDescriptor.FACTORY);
diff --git a/hyracks-fullstack/hyracks/hyracks-data/hyracks-data-std/src/main/java/org/apache/hyracks/data/std/primitive/UTF8StringPointable.java b/hyracks-fullstack/hyracks/hyracks-data/hyracks-data-std/src/main/java/org/apache/hyracks/data/std/primitive/UTF8StringPointable.java
index 530dab4..81afdf6 100644
--- a/hyracks-fullstack/hyracks/hyracks-data/hyracks-data-std/src/main/java/org/apache/hyracks/data/std/primitive/UTF8StringPointable.java
+++ b/hyracks-fullstack/hyracks/hyracks-data/hyracks-data-std/src/main/java/org/apache/hyracks/data/std/primitive/UTF8StringPointable.java
@@ -474,6 +474,25 @@
         return true;
     }
 
+    /*
+     * Appends {@code numCodePoints} code points from the given source string {@code src} to the {@code builder}.
+     * {@code src} is shorter than {@code numCodePoints}, it repeats from the beginning of {@code src} as needed
+     * until the required number of code points have been appended.
+     */
+    public static void append(UTF8StringPointable src, int numCodePoints, UTF8StringBuilder builder, GrowableArray out)
+            throws IOException {
+        int utfLen = src.getUTF8Length();
+        int byteIdx = 0;
+        while (numCodePoints > 0) {
+            if (byteIdx == utfLen) {
+                byteIdx = 0;
+            }
+            builder.appendCodePoint(src.codePointAt(src.getMetaDataLength() + byteIdx));
+            numCodePoints--;
+            byteIdx += src.codePointSize(src.getMetaDataLength() + byteIdx);
+        }
+    }
+
     public void substrBefore(UTF8StringPointable match, UTF8StringBuilder builder, GrowableArray out)
             throws IOException {
         substrBefore(this, match, builder, out);