blob: af0a43fdad00640952253974d3e4d6e01e99b4e6 [file] [log] [blame]
// 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.
suite("test_pythonudf_module_scalar") {
// Comprehensive test for scalar Python UDF using module mode
def pyPath = """${context.file.parent}/udf_scripts/python_udf_scalar_ops.zip"""
scp_udf_file_to_all_be(pyPath)
def runtime_version = "3.10.12"
log.info("Python module path: ${pyPath}".toString())
try {
// Create test table with diverse data types
sql """ DROP TABLE IF EXISTS scalar_module_test_table; """
sql """
CREATE TABLE scalar_module_test_table (
id INT,
int_a INT,
int_b INT,
int_c INT,
double_a DOUBLE,
double_b DOUBLE,
string_a STRING,
string_b STRING,
bool_a BOOLEAN,
bool_b BOOLEAN,
date_a DATE,
date_b DATE
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES("replication_num" = "1");
"""
sql """
INSERT INTO scalar_module_test_table VALUES
(1, 10, 20, 30, 100.0, 10.0, 'hello world', 'test@example.com', true, true, '2024-01-15', '2024-01-20'),
(2, 5, 15, 25, 200.0, 20.0, 'foo bar baz', 'user@domain.com', false, true, '2024-02-10', '2024-03-15'),
(3, 100, 50, 25, 150.0, 0.0, 'racecar', 'admin@test.org', true, false, '2023-12-01', '2024-01-01'),
(4, 7, 3, 11, 80.0, 5.0, 'a man a plan a canal panama', 'info@company.net', false, false, '2024-06-15', '2024-06-15'),
(5, 17, 19, 23, 300.0, 15.0, 'python udf test', 'contact@site.io', true, true, '2024-03-01', '2024-12-31');
"""
// ==================== Numeric Operations Tests ====================
// Test 1: Add three numbers
sql """ DROP FUNCTION IF EXISTS py_add_three(INT, INT, INT); """
sql """
CREATE FUNCTION py_add_three(INT, INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.add_three_numbers",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_add_three """
SELECT
id,
int_a, int_b, int_c,
py_add_three(int_a, int_b, int_c) AS result
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 2: Safe division with precision
sql """ DROP FUNCTION IF EXISTS py_safe_div(DOUBLE, DOUBLE, INT); """
sql """
CREATE FUNCTION py_safe_div(DOUBLE, DOUBLE, INT)
RETURNS DOUBLE
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.safe_divide_with_precision",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_safe_div """
SELECT
id,
double_a, double_b,
py_safe_div(double_a, double_b, 2) AS result
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 3: Calculate discount price
sql """ DROP FUNCTION IF EXISTS py_discount(DOUBLE, DOUBLE); """
sql """
CREATE FUNCTION py_discount(DOUBLE, DOUBLE)
RETURNS DOUBLE
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.calculate_discount_price",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_discount """
SELECT
id,
double_a,
py_discount(double_a, 10.0) AS price_10_off,
py_discount(double_a, 25.0) AS price_25_off
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 4: Compound interest
sql """ DROP FUNCTION IF EXISTS py_compound_interest(DOUBLE, DOUBLE, INT); """
sql """
CREATE FUNCTION py_compound_interest(DOUBLE, DOUBLE, INT)
RETURNS DOUBLE
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.compound_interest",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_compound_interest """
SELECT
id,
double_a,
py_compound_interest(double_a, 5.0, 10) AS future_value
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 5: Calculate BMI
sql """ DROP FUNCTION IF EXISTS py_bmi(DOUBLE, DOUBLE); """
sql """
CREATE FUNCTION py_bmi(DOUBLE, DOUBLE)
RETURNS DOUBLE
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.calculate_bmi",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_bmi """
SELECT
id,
py_bmi(70.0, 1.75) AS bmi_normal,
py_bmi(90.0, 1.75) AS bmi_overweight
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 6: Fibonacci number
sql """ DROP FUNCTION IF EXISTS py_fibonacci(INT); """
sql """
CREATE FUNCTION py_fibonacci(INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.fibonacci",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_fibonacci """
SELECT
id,
int_a,
py_fibonacci(int_a) AS fib_result
FROM scalar_module_test_table
WHERE int_a <= 20
ORDER BY id;
"""
// Test 7: Is prime number
sql """ DROP FUNCTION IF EXISTS py_is_prime(INT); """
sql """
CREATE FUNCTION py_is_prime(INT)
RETURNS BOOLEAN
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.is_prime",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_is_prime """
SELECT
id,
int_a, int_b, int_c,
py_is_prime(int_a) AS a_is_prime,
py_is_prime(int_b) AS b_is_prime,
py_is_prime(int_c) AS c_is_prime
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 8: GCD (Greatest Common Divisor)
sql """ DROP FUNCTION IF EXISTS py_gcd(INT, INT); """
sql """
CREATE FUNCTION py_gcd(INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.gcd",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_gcd """
SELECT
id,
int_a, int_b,
py_gcd(int_a, int_b) AS gcd_result
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 9: LCM (Least Common Multiple)
sql """ DROP FUNCTION IF EXISTS py_lcm(INT, INT); """
sql """
CREATE FUNCTION py_lcm(INT, INT)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.lcm",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_lcm """
SELECT
id,
int_a, int_b,
py_lcm(int_a, int_b) AS lcm_result
FROM scalar_module_test_table
ORDER BY id;
"""
// ==================== String Operations Tests ====================
// Test 10: Reverse string
sql """ DROP FUNCTION IF EXISTS py_reverse(STRING); """
sql """
CREATE FUNCTION py_reverse(STRING)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.reverse_string",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_reverse """
SELECT
id,
string_a,
py_reverse(string_a) AS reversed
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 11: Count vowels
sql """ DROP FUNCTION IF EXISTS py_count_vowels(STRING); """
sql """
CREATE FUNCTION py_count_vowels(STRING)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.count_vowels",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_count_vowels """
SELECT
id,
string_a,
py_count_vowels(string_a) AS vowel_count
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 12: Count words
sql """ DROP FUNCTION IF EXISTS py_count_words(STRING); """
sql """
CREATE FUNCTION py_count_words(STRING)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.count_words",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_count_words """
SELECT
id,
string_a,
py_count_words(string_a) AS word_count
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 13: Capitalize words
sql """ DROP FUNCTION IF EXISTS py_capitalize(STRING); """
sql """
CREATE FUNCTION py_capitalize(STRING)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.capitalize_words",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_capitalize """
SELECT
id,
string_a,
py_capitalize(string_a) AS capitalized
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 14: Is palindrome
sql """ DROP FUNCTION IF EXISTS py_is_palindrome(STRING); """
sql """
CREATE FUNCTION py_is_palindrome(STRING)
RETURNS BOOLEAN
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.is_palindrome",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_is_palindrome """
SELECT
id,
string_a,
py_is_palindrome(string_a) AS is_palindrome
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 15: String similarity
sql """ DROP FUNCTION IF EXISTS py_similarity(STRING, STRING); """
sql """
CREATE FUNCTION py_similarity(STRING, STRING)
RETURNS DOUBLE
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.string_similarity",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_similarity """
SELECT
id,
string_a,
py_similarity(string_a, 'hello') AS similarity_to_hello
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 16: Mask email
sql """ DROP FUNCTION IF EXISTS py_mask_email(STRING); """
sql """
CREATE FUNCTION py_mask_email(STRING)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.mask_email",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_mask_email """
SELECT
id,
string_b,
py_mask_email(string_b) AS masked_email
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 17: Extract domain from email
sql """ DROP FUNCTION IF EXISTS py_extract_domain(STRING); """
sql """
CREATE FUNCTION py_extract_domain(STRING)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.extract_domain",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_extract_domain """
SELECT
id,
string_b,
py_extract_domain(string_b) AS domain
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 18: Levenshtein distance
sql """ DROP FUNCTION IF EXISTS py_levenshtein(STRING, STRING); """
sql """
CREATE FUNCTION py_levenshtein(STRING, STRING)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.levenshtein_distance",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_levenshtein """
SELECT
id,
string_a,
py_levenshtein(string_a, 'hello world') AS edit_distance
FROM scalar_module_test_table
ORDER BY id;
"""
// ==================== Date/Time Operations Tests ====================
// Test 19: Days between dates
sql """ DROP FUNCTION IF EXISTS py_days_between(DATE, DATE); """
sql """
CREATE FUNCTION py_days_between(DATE, DATE)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.days_between_dates",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_days_between """
SELECT
id,
date_a, date_b,
py_days_between(date_a, date_b) AS days_diff
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 20: Is weekend
sql """ DROP FUNCTION IF EXISTS py_is_weekend(DATE); """
sql """
CREATE FUNCTION py_is_weekend(DATE)
RETURNS BOOLEAN
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.is_weekend",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_is_weekend """
SELECT
id,
date_a,
py_is_weekend(date_a) AS is_weekend
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 21: Get quarter
sql """ DROP FUNCTION IF EXISTS py_get_quarter(DATE); """
sql """
CREATE FUNCTION py_get_quarter(DATE)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.get_quarter",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_get_quarter """
SELECT
id,
date_a,
py_get_quarter(date_a) AS quarter
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 22: Age in years
sql """ DROP FUNCTION IF EXISTS py_age(DATE, DATE); """
sql """
CREATE FUNCTION py_age(DATE, DATE)
RETURNS INT
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.age_in_years",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_age """
SELECT
id,
py_age('1990-01-01', date_a) AS age
FROM scalar_module_test_table
ORDER BY id;
"""
// ==================== Boolean/Conditional Operations Tests ====================
// Test 23: Is in range
sql """ DROP FUNCTION IF EXISTS py_in_range(INT, INT, INT); """
sql """
CREATE FUNCTION py_in_range(INT, INT, INT)
RETURNS BOOLEAN
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.is_in_range",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_in_range """
SELECT
id,
int_a,
py_in_range(int_a, 10, 50) AS in_range_10_50
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 24: XOR operation
sql """ DROP FUNCTION IF EXISTS py_xor(BOOLEAN, BOOLEAN); """
sql """
CREATE FUNCTION py_xor(BOOLEAN, BOOLEAN)
RETURNS BOOLEAN
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.xor_operation",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_xor """
SELECT
id,
bool_a, bool_b,
py_xor(bool_a, bool_b) AS xor_result
FROM scalar_module_test_table
ORDER BY id;
"""
// ==================== Complex/Mixed Operations Tests ====================
// Test 25: Calculate grade
sql """ DROP FUNCTION IF EXISTS py_grade(DOUBLE); """
sql """
CREATE FUNCTION py_grade(DOUBLE)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.calculate_grade",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_grade """
SELECT
id,
double_a,
py_grade(double_a) AS grade
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 26: Categorize age
sql """ DROP FUNCTION IF EXISTS py_categorize_age(INT); """
sql """
CREATE FUNCTION py_categorize_age(INT)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.categorize_age",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_categorize_age """
SELECT
id,
int_a,
py_categorize_age(int_a) AS age_category
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 27: Calculate tax
sql """ DROP FUNCTION IF EXISTS py_tax(DOUBLE, DOUBLE); """
sql """
CREATE FUNCTION py_tax(DOUBLE, DOUBLE)
RETURNS DOUBLE
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.calculate_tax",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_tax """
SELECT
id,
double_a,
py_tax(double_a, 15.0) AS tax_15_percent
FROM scalar_module_test_table
ORDER BY id;
"""
// Test 28: Truncate string with suffix
sql """ DROP FUNCTION IF EXISTS py_truncate(STRING, INT, STRING); """
sql """
CREATE FUNCTION py_truncate(STRING, INT, STRING)
RETURNS STRING
PROPERTIES (
"type" = "PYTHON_UDF",
"file" = "file://${pyPath}",
"symbol" = "python_udf_scalar_ops.truncate_string",
"runtime_version" = "${runtime_version}",
"always_nullable" = "true"
);
"""
qt_truncate """
SELECT
id,
string_a,
py_truncate(string_a, 10, '...') AS truncated
FROM scalar_module_test_table
ORDER BY id;
"""
// ==================== Edge Cases and NULL Handling Tests ====================
// Test 29: NULL handling in numeric operations
sql """ DROP TABLE IF EXISTS null_test_table; """
sql """
CREATE TABLE null_test_table (
id INT,
val1 INT,
val2 INT,
val3 INT
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES("replication_num" = "1");
"""
sql """
INSERT INTO null_test_table VALUES
(1, 10, 20, 30),
(2, NULL, 20, 30),
(3, 10, NULL, 30),
(4, 10, 20, NULL),
(5, NULL, NULL, NULL);
"""
qt_null_handling """
SELECT
id,
val1, val2, val3,
py_add_three(val1, val2, val3) AS sum_result
FROM null_test_table
ORDER BY id;
"""
// Test 30: Empty string handling
sql """ DROP TABLE IF EXISTS string_edge_test; """
sql """
CREATE TABLE string_edge_test (
id INT,
str_val STRING
) ENGINE=OLAP
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES("replication_num" = "1");
"""
sql """
INSERT INTO string_edge_test VALUES
(1, 'normal string'),
(2, ''),
(3, ' '),
(4, 'a'),
(5, NULL);
"""
qt_string_edge """
SELECT
id,
str_val,
py_reverse(str_val) AS reversed,
py_count_vowels(str_val) AS vowels,
py_count_words(str_val) AS words
FROM string_edge_test
ORDER BY id;
"""
} finally {
// Cleanup all functions
try_sql("DROP FUNCTION IF EXISTS py_add_three(INT, INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_safe_div(DOUBLE, DOUBLE, INT);")
try_sql("DROP FUNCTION IF EXISTS py_discount(DOUBLE, DOUBLE);")
try_sql("DROP FUNCTION IF EXISTS py_compound_interest(DOUBLE, DOUBLE, INT);")
try_sql("DROP FUNCTION IF EXISTS py_bmi(DOUBLE, DOUBLE);")
try_sql("DROP FUNCTION IF EXISTS py_fibonacci(INT);")
try_sql("DROP FUNCTION IF EXISTS py_is_prime(INT);")
try_sql("DROP FUNCTION IF EXISTS py_gcd(INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_lcm(INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_reverse(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_count_vowels(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_count_words(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_capitalize(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_is_palindrome(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_similarity(STRING, STRING);")
try_sql("DROP FUNCTION IF EXISTS py_mask_email(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_extract_domain(STRING);")
try_sql("DROP FUNCTION IF EXISTS py_levenshtein(STRING, STRING);")
try_sql("DROP FUNCTION IF EXISTS py_days_between(DATE, DATE);")
try_sql("DROP FUNCTION IF EXISTS py_is_weekend(DATE);")
try_sql("DROP FUNCTION IF EXISTS py_get_quarter(DATE);")
try_sql("DROP FUNCTION IF EXISTS py_age(DATE, DATE);")
try_sql("DROP FUNCTION IF EXISTS py_in_range(INT, INT, INT);")
try_sql("DROP FUNCTION IF EXISTS py_xor(BOOLEAN, BOOLEAN);")
try_sql("DROP FUNCTION IF EXISTS py_grade(DOUBLE);")
try_sql("DROP FUNCTION IF EXISTS py_categorize_age(INT);")
try_sql("DROP FUNCTION IF EXISTS py_tax(DOUBLE, DOUBLE);")
try_sql("DROP FUNCTION IF EXISTS py_truncate(STRING, INT, STRING);")
// Cleanup tables
try_sql("DROP TABLE IF EXISTS scalar_module_test_table;")
try_sql("DROP TABLE IF EXISTS null_test_table;")
try_sql("DROP TABLE IF EXISTS string_edge_test;")
}
}