存储 URL 到 MySQL

序言

如果无法读写数据库,那么一门编程语言的实用性将会大打折扣。好在,SWI-Prolog 对这方面也提供了支持,接下来就这方面进行讲解。

连接 MySQL

查阅 SWI-Prolog 的文档可以看到,如果要读取 MySQL,可以使用这几个谓词:

  • odbc_driver_connect,用来连接数据库;
  • odbc_query,执行查询的 SQL,获取结果;
  • odbc_disconnect,断开数据库连接。

一段查询 MySQL 版本号信息的示例代码如下

:- use_module(library(odbc), [odbc_disconnect/1, odbc_driver_connect/3, odbc_query/3]).

:- initialization(main, main).

main(_) :-
    Dsn = "DRIVER={/usr/local/mysql-connector-odbc-8.0.33-macos13-x86-64bit/lib/libmyodbc8a.so};String Types=Unicode;password=1234567;port=3306;server=localhost;user=shorten",
    odbc_driver_connect(Dsn, Connection, []),
    Sql = "SELECT VERSION()",
    odbc_query(Connection, Sql, row(Version)),
    odbc_disconnect(Connection),
    format("Version is ~s~n", [Version]).

效果如下图所示

查询MySQL版本号 查询MySQL版本号

MySQL ODBC 驱动

MySQL 的 ODBC 驱动文件需要自己安装。先到这里下载

下载ODBC驱动 下载ODBC驱动

安装完毕后,通过iODBC Administrator64可以看到驱动的具体文件路径

查看驱动属性 查看驱动属性

驱动文件绝对路径 驱动文件绝对路径

这样就获得了驱动文件的具体路径,可以用到Dsn中了。

读写 MySQL

假设用于存储短链信息的表名为t_short_url,其表结构如下

CREATE TABLE `t_short_url` (
    `id` BIGINT NOT NULL AUTO_INCREMENT,
    `url` VARCHAR(256) NOT NULL COMMENT '短链对应的原始链接',
    `ctime` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    `mtime` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    UNIQUE INDEX `ux__url` (`url`)
) AUTO_INCREMENT=1000001;

那么可以像下面这样子改写谓词url_to_id,使其从基于文件变成基于 MySQL 来存储短链信息。

:- module(url_to_id, [url_to_id/2]).

:- use_module(library(odbc), [odbc_disconnect/1, odbc_driver_connect/3, odbc_query/3]).

% 读写文件来管理短链。
url_to_id(Url, Id) :-
    string(Url),
    var(Id),  % Id 不再需要由外部实例化,因此这里要求其为变量。
    % 连接数据库。
    Dsn = "DRIVER={/usr/local/mysql-connector-odbc-8.0.33-macos13-x86-64bit/lib/libmyodbc8a.so};String Types=Unicode;password=1234567;port=3306;server=localhost;user=shorten",
    odbc_driver_connect(Dsn, Connection, []),
    % 用参数化来构造 INSERT 语句。
    odbc_prepare(Connection, 'INSERT INTO `test`.`t_short_url` SET `url` = ?', [default], Statement),
    odbc_execute(Statement, [Url]),
    % 获取新行的 id 列的值。
    odbc_query(Connection, 'SELECT LAST_INSERT_ID()', row(Id)),
    % 断开连接。
    odbc_disconnect(Connection).
url_to_id(Url, Id) :-
    var(Url),
    integer(Id),
    % 连接数据库。
    Dsn = "DRIVER={/usr/local/mysql-connector-odbc-8.0.33-macos13-x86-64bit/lib/libmyodbc8a.so};String Types=Unicode;password=1234567;port=3306;server=localhost;user=shorten",
    odbc_driver_connect(Dsn, Connection, []),
    % 在输入安全的情况下直接拼接 SQL 字符串。
    format(string(Sql), 'SELECT `url` FROM `test`.`t_short_url` WHERE `id` = ~d', [Id]),
    % 获取给定 ID 的行的原始 URL。
    odbc_query(Connection, Sql, row(Url)),
    % 断开连接。
    odbc_disconnect(Connection).

可以单独测试这个模块的效果,如下图所示

url_to_id改写后的效果 url_to_id改写后的效果

而在调用谓词url_to_id的位置,则不再需要事先计算好Id的值了,将下列代码删除

    % 姑且用当前时间戳来作为 ID。
    get_time(TimeStamp),
    Id is truncate(TimeStamp),

然后 HTTP 服务便可以照常运作了。