做个奇怪的东西-Golang快速实现功能、c封装成PostgreSQL的扩展
前言
以前写过很多数据库方面的存储过程之类的,应该算是小型公司的一种全栈要求,什么都要会,最多的话一个系统写了一两百个函数或存储过程。最近因为在改系统,想起来以前的区号和工作时间等,都是纯sql或者在开发语言层面干掉了,那么不如写成PostgreSQL的function,本来应该这样,但是由于以前的改动大,要多栈真的脑子变来变去,就不了了之。
操作
首先看下以下两个表结构:
用途是将基本的手机号段和区号以及省市信息记录到nway_base_mobile_location表中;在nway_area_plan中,定义这条规则所要使用到的区号列表,用于和nway_base_mobile_location中的district_no匹配。
CREATE TABLE IF NOT EXISTS public.nway_base_mobile_location
(
no character varying(7) COLLATE pg_catalog."default" NOT NULL,
location character varying(50) COLLATE pg_catalog."default",
district_no character varying(100) COLLATE pg_catalog."default"
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.nway_base_mobile_location
OWNER to postgres;
COMMENT ON TABLE public.nway_base_mobile_location
IS '上海宁卫信息技术有限公司,李浩。手机归属地';
COMMENT ON COLUMN public.nway_base_mobile_location.no
IS '手机号段';
COMMENT ON COLUMN public.nway_base_mobile_location.location
IS '所在地';
COMMENT ON COLUMN public.nway_base_mobile_location.district_no
IS '区号';
-- Index: PK_NO_BASE_MOBILE_LOCATION
-- DROP INDEX IF EXISTS public."PK_NO_BASE_MOBILE_LOCATION";
CREATE INDEX IF NOT EXISTS "PK_NO_BASE_MOBILE_LOCATION"
ON public.nway_base_mobile_location USING btree
(no COLLATE pg_catalog."C" varchar_ops ASC NULLS LAST)
TABLESPACE pg_default;
-- Index: base_mobile_location_district_no
-- DROP INDEX IF EXISTS public.base_mobile_location_district_no;
CREATE INDEX IF NOT EXISTS base_mobile_location_district_no
ON public.nway_base_mobile_location USING btree
(district_no COLLATE pg_catalog."default" ASC NULLS LAST)
TABLESPACE pg_default;
-- Index: base_mobile_location_no
-- DROP INDEX IF EXISTS public.base_mobile_location_no;
CREATE INDEX IF NOT EXISTS base_mobile_location_no
ON public.nway_base_mobile_location USING btree
(no COLLATE pg_catalog."default" ASC NULLS LAST)
TABLESPACE pg_default;
------------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.nway_area_plan
(
id bigint NOT NULL DEFAULT nextval('nway_area_plan_id_seq'::regclass),
plan_name character varying(100) COLLATE pg_catalog."default" NOT NULL DEFAULT ''::character varying,
plan_desc text COLLATE pg_catalog."default",
node_id bigint,
district_no text COLLATE pg_catalog."default" DEFAULT ''::text,
CONSTRAINT nway_area_plan_pkey PRIMARY KEY (id)
)
WITH (
OIDS = FALSE
)
TABLESPACE pg_default;
ALTER TABLE IF EXISTS public.nway_area_plan
OWNER to postgres;
COMMENT ON TABLE public.nway_area_plan
IS '区域策略';
COMMENT ON COLUMN public.nway_area_plan.district_no
IS '电话区号列表';
以前的做法是使用Python或golang写个独立的应用,进行把xls等手机号段表单独运行导入,不过前几天看到 https://zhmin.github.io/ 上的一篇《Postgresql 编写自定义 C 函数》,于是就想是不是写成一个PostgreSQL扩展会好玩些,但是面临的问题也比较现实:1. c语言的运算部分,代码写起来费劲;2. c语言在各类扩展中质量参差不齐,容易陷入调试死胡同;3. c语言各类应用的库都不如GO、python等相对完备。所以采用Go开发成动静态库,再用c简单调用一层,生成对应的PostgreSQL扩展动态库(由Go语言直接生成也可以,不过那要折腾,还不如直接c包一层)。
代码
将开发的Go的库通过
go build --ldflags "-s -w" -buildmode=c-shared -o libImptFile.so main.go
生成so及头文件
//libImptFile.h
//有一堆的定义等略过
#ifdef __cplusplus
extern "C" {
#endif
//入参
//dbstring:数据库连接字符串
//file_path:本地的xsls或xls等excel表
//no_idx:号段字段在excel表格中的第几列
//location_idx:位置字段在excel表格中的第几列
//district_no_idx:区号字段在excel表格中的第几列
//err_msg:错误时返回的字符串说明,不超过256长度
//返回值
//整型:0完成;-1文件不存在;-2文件格式读取不出;1写入库中时异常
extern int ImportMobLocFromFile(GoString dbstring, GoString file_path, GoInt no_idx, GoInt location_idx, GoInt district_no_idx, char* err_msg);
#ifdef __cplusplus
}
#endif
然后再用c来调用
#include "libImptFile.h"
#include "postgres.h"
#include "fmgr.h"
PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(impt_file_func);
Datum impt_file_func(PG_FUNCTION_ARGS)
{
char* dbstr=PG_GETARG_CSTRING(0);
char* fp = PG_GETARG_CSTRING(1);
int32 n = PG_GETARG_INT32(2);
int32 l = PG_GETARG_INT32(3);
int32 d = PG_GETARG_INT32(4);
char* err_msg=(char*) palloc(256);
//需要加个套,Gostring和GoInt
int32 r=0;
GoString dbstring={dbstr,strlen(dbstr)};
GoString file_path={fp,strlen(fp)};
GoInt no_idx=(GoInt)n;
GoInt location_idx = (GoInt)l;
GoInt district_no_idx=(GoInt)d;
r = ImportMobLocFromFile(dbstring,file_path,no_idx,location_idx,district_no_idx,err_msg);
if (r!=0){
printf("call ImportMobLocFromFile failed:%s",err_msg );
}
pfree(err_msg);
PG_RETURN_INT32(r);
}
再然后编译后调用
gcc -fPIC -c impt_file_func.c -I/usr/include/pgsql/server/ -shared -o impt_file_func.so
接着在psql中
CREATE FUNCTION impt_file_func(TEXT,TEXT,integer,integer,integer) RETURNS integer
AS '/var/lib/11/pgsql/impt_file_func.so', 'impt_file_func' LANGUAGE C STRICT;
OK,那么就去调用吧
select impt_file_func("user=postgres dbname=cloudcc password=Nway2017 host=127.0.0.1 port=5432 sslmode=disable","/home/20221011.xlsx",0,1,2) as nway_result;