1. 做个奇怪的东西-Golang快速实现功能、c封装成PostgreSQL的扩展:
  2. 宁卫通信
  3. 新闻动态
  4. 宁卫新闻
  5. 做个奇怪的东西-Golang快速实现功能、c封装成PostgreSQL的扩展

做个奇怪的东西-Golang快速实现功能、c封装成PostgreSQL的扩展

前言

       以前写过很多数据库方面的存储过程之类的,应该算是小型公司的一种全栈要求,什么都要会,最多的话一个系统写了一两百个函数或存储过程。最近因为在改系统,想起来以前的区号和工作时间等,都是纯sql或者在开发语言层面干掉了,那么不如写成PostgreSQL的function,本来应该这样,但是由于以前的改动大,要多栈真的脑子变来变去,就不了了之。

操作

       首先看下以下两个表结构: 
   用途是将基本的手机号段和区号以及省市信息记录到nway_base_mobile_location表中;在nway_area_plan中,定义这条规则所要使用到的区号列表,用于和nway_base_mobile_location中的district_no匹配。


  1. CREATE TABLE IF NOT EXISTS public.nway_base_mobile_location

  2. (

  3. no character varying(7) COLLATE pg_catalog."default" NOT NULL,

  4. location character varying(50) COLLATE pg_catalog."default",

  5. district_no character varying(100) COLLATE pg_catalog."default"

  6. )

  7. WITH (

  8. OIDS = FALSE

  9. )

  10. TABLESPACE pg_default;


  11. ALTER TABLE IF EXISTS public.nway_base_mobile_location

  12. OWNER to postgres;


  13. COMMENT ON TABLE public.nway_base_mobile_location

  14. IS '上海宁卫信息技术有限公司,李浩。手机归属地';


  15. COMMENT ON COLUMN public.nway_base_mobile_location.no

  16. IS '手机号段';


  17. COMMENT ON COLUMN public.nway_base_mobile_location.location

  18. IS '所在地';


  19. COMMENT ON COLUMN public.nway_base_mobile_location.district_no

  20. IS '区号';

  21. -- Index: PK_NO_BASE_MOBILE_LOCATION


  22. -- DROP INDEX IF EXISTS public."PK_NO_BASE_MOBILE_LOCATION";


  23. CREATE INDEX IF NOT EXISTS "PK_NO_BASE_MOBILE_LOCATION"

  24. ON public.nway_base_mobile_location USING btree

  25. (no COLLATE pg_catalog."C" varchar_ops ASC NULLS LAST)

  26. TABLESPACE pg_default;

  27. -- Index: base_mobile_location_district_no


  28. -- DROP INDEX IF EXISTS public.base_mobile_location_district_no;


  29. CREATE INDEX IF NOT EXISTS base_mobile_location_district_no

  30. ON public.nway_base_mobile_location USING btree

  31. (district_no COLLATE pg_catalog."default" ASC NULLS LAST)

  32. TABLESPACE pg_default;

  33. -- Index: base_mobile_location_no


  34. -- DROP INDEX IF EXISTS public.base_mobile_location_no;


  35. CREATE INDEX IF NOT EXISTS base_mobile_location_no

  36. ON public.nway_base_mobile_location USING btree

  37. (no COLLATE pg_catalog."default" ASC NULLS LAST)

  38. TABLESPACE pg_default;


  39. ------------------------------------------------------------------------------

  40. CREATE TABLE IF NOT EXISTS public.nway_area_plan

  41. (

  42. id bigint NOT NULL DEFAULT nextval('nway_area_plan_id_seq'::regclass),

  43. plan_name character varying(100) COLLATE pg_catalog."default" NOT NULL DEFAULT ''::character varying,

  44. plan_desc text COLLATE pg_catalog."default",

  45. node_id bigint,

  46. district_no text COLLATE pg_catalog."default" DEFAULT ''::text,

  47. CONSTRAINT nway_area_plan_pkey PRIMARY KEY (id)

  48. )

  49. WITH (

  50. OIDS = FALSE

  51. )

  52. TABLESPACE pg_default;


  53. ALTER TABLE IF EXISTS public.nway_area_plan

  54. OWNER to postgres;


  55. COMMENT ON TABLE public.nway_area_plan

  56. IS '区域策略';


  57. COMMENT ON COLUMN public.nway_area_plan.district_no

  58. 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及头文件

  1. //libImptFile.h

  2. //有一堆的定义等略过

  3. #ifdef __cplusplus

  4. extern "C" {

  5. #endif


  6. //入参

  7. //dbstring:数据库连接字符串

  8. //file_path:本地的xsls或xls等excel表

  9. //no_idx:号段字段在excel表格中的第几列

  10. //location_idx:位置字段在excel表格中的第几列

  11. //district_no_idx:区号字段在excel表格中的第几列

  12. //err_msg:错误时返回的字符串说明,不超过256长度

  13. //返回值

  14. //整型:0完成;-1文件不存在;-2文件格式读取不出;1写入库中时异常

  15. extern int ImportMobLocFromFile(GoString dbstring, GoString file_path, GoInt no_idx, GoInt location_idx, GoInt district_no_idx, char* err_msg);


  16. #ifdef __cplusplus

  17. }

  18. #endif

然后再用c来调用

  1. #include "libImptFile.h"


  2. #include "postgres.h"

  3. #include "fmgr.h"


  4. PG_MODULE_MAGIC;


  5. PG_FUNCTION_INFO_V1(impt_file_func);

  6. Datum impt_file_func(PG_FUNCTION_ARGS)

  7. {

  8. char* dbstr=PG_GETARG_CSTRING(0);

  9. char* fp = PG_GETARG_CSTRING(1);


  10. int32 n = PG_GETARG_INT32(2);

  11. int32 l = PG_GETARG_INT32(3);

  12. int32 d = PG_GETARG_INT32(4);


  13. char* err_msg=(char*) palloc(256);

  14. //需要加个套,Gostring和GoInt

  15. int32 r=0;

  16. GoString dbstring={dbstr,strlen(dbstr)};

  17. GoString file_path={fp,strlen(fp)};

  18. GoInt no_idx=(GoInt)n;

  19. GoInt location_idx = (GoInt)l;

  20. GoInt district_no_idx=(GoInt)d;

  21. r = ImportMobLocFromFile(dbstring,file_path,no_idx,location_idx,district_no_idx,err_msg);

  22. if (r!=0){

  23. printf("call ImportMobLocFromFile failed:%s",err_msg );


  24. }

  25. pfree(err_msg);

  26. PG_RETURN_INT32(r);

  27. }

再然后编译后调用

  1. gcc -fPIC -c impt_file_func.c -I/usr/include/pgsql/server/ -shared -o impt_file_func.so

接着在psql中

  1. CREATE FUNCTION impt_file_func(TEXT,TEXT,integer,integer,integer) RETURNS integer

  2. AS '/var/lib/11/pgsql/impt_file_func.so', 'impt_file_func' LANGUAGE C STRICT;

OK,那么就去调用吧

  1. 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;