Apache AGE容器自动化测试中cypher函数未识别问题
问题背景
我正在开发基于Apache AGE的Python模块,需要编写单元测试。为此写了bash脚本实现自动化流程:拉取apache/age镜像启动Docker容器,复制SQL配置文件到容器内执行,运行Python测试脚本,最后停止并删除容器,方便快速排查错误。
现有自动化脚本
test_script.sh
#!/bin/bash # 启动Apache AGE容器 docker pull apache/age docker run \ --name myPostgresDb \ -p 5455:5432 \ -e POSTGRES_USER=postgresUser \ -e POSTGRES_PASSWORD=postgresPW \ -e POSTGRES_DB=postgresDB \ -d \ apache/age sleep 3 # 复制SQL脚本到容器内 docker cp tests/setup_age.sql myPostgresDb:/setup_age.sql sleep 1 # 在容器内执行SQL脚本 docker exec -i myPostgresDb psql -U postgresUser -d postgresDB -f /setup_age.sql sleep 1 # 执行Python测试脚本 python3 -m tests.test # 停止并删除容器 docker stop myPostgresDb docker rm myPostgresDb
setup_age.sql
LOAD 'age'; SET search_path = ag_catalog, "$user", public;
错误情况
SQL脚本执行成功,但Python测试脚本执行以下查询时:
SELECT * FROM cypher('graph', $$ CREATE (n:label) $$) as (n agtype);
出现错误:
psycopg2.errors.UndefinedFunction: function cypher(unknown, unknown) does not exist
LINE 2: SELECT * FROM cypher('graph', $$
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
手动进入容器终端执行配置语句和测试查询可正常运行,但自动化流程无法正常工作,手动操作耗时较长。
补充脚本输出
Using default tag: latest latest: Pulling from apache/age Digest: sha256:5134806b5de7d16e0123fba928f7992064b4eebaeb9f2cae225ad4f5b5475a33 Status: Image is up to date for apache/age:latest docker.io/apache/age:latest What's Next? View a summary of image vulnerabilities and recommendations → docker scout quickview apache/age WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested 7bbe43007dbac2185ef6da73e4e503e61deade3651f470c04611c6531c41a3f0 Successfully copied 2.05kB to myPostgresDb:/setup_age.sql LOAD SET
解决方案
核心问题是会话级别的search_path设置未持久化:通过docker exec执行的SET search_path仅对当前psql会话生效,而Python测试脚本是新建的数据库连接,不会继承该设置。
方法1:在Python测试脚本中配置会话参数
在建立数据库连接后,先执行加载扩展和设置search_path的语句,确保当前会话生效:
import psycopg2 # 建立数据库连接 conn = psycopg2.connect( dbname="postgresDB", user="postgresUser", password="postgresPW", host="localhost", port="5455" ) cur = conn.cursor() # 加载AGE扩展并设置search_path cur.execute("LOAD 'age';") cur.execute("SET search_path = ag_catalog, \"$user\", public;") conn.commit() # 执行Cypher查询 cur.execute(""" SELECT * FROM cypher('graph', $$ CREATE (n:label) $$) as (n agtype); """)
方法2:全局持久化search_path配置
修改自动化脚本,将search_path写入PostgreSQL的配置文件,使所有新连接自动应用该设置:
在test_script.sh的docker cp步骤之后、docker exec执行SQL脚本之前,添加以下内容:
# 修改postgresql.conf,添加全局search_path配置 docker exec -i myPostgresDb bash -c "echo 'search_path = ag_catalog, \"\$user\", public' >> /var/lib/postgresql/data/postgresql.conf" # 重启PostgreSQL服务使配置生效 docker exec -i myPostgresDb pg_ctl -D /var/lib/postgresql/data restart sleep 2
额外优化建议
- 脚本中的固定
sleep时间可能不足以让PostgreSQL完全启动(尤其是跨平台运行镜像时),可以替换为等待数据库就绪的逻辑:
# 等待PostgreSQL服务就绪 until docker exec myPostgresDb psql -U postgresUser -d postgresDB -c "SELECT 1;" > /dev/null 2>&1; do sleep 1 done
这样能避免固定sleep的不确定性,确保数据库完全启动后再执行后续操作。
内容的提问来源于stack exchange,提问作者Matheus Farias

