Marvin's Blog【程式人生】

Ability will never catch up with the demand for it

19 Dec 2020

把FileMaker当作ODBC和JDBC数据源

Filemaker是一款数据库设计工具,也是一款数据库UI设计工具。有点类似于MS的Access,但是其UI设计功能更强大,并且跟iOS和macOS结合更紧密。Filemaker支持作为ODBC或者JDBC数据源使用。下文在不用区分ODBC以及JDBC的时候,使用xDBC替代。

通过Software Update: FileMaker xDBC client drivers for FileMaker,可以下载FileMaker的xDBC驱动。

ODBC

ODBC最早是由微软提出来的,用于抽象不同数据库接口间的差异。ODBC基于CS架构,其编程接口则是采用C/C++。在64位的macOS上使用ODBC,可能需要用到第三方的64-bit ODBC Manager。因为macOS本身放弃了对ODBC Manager的支持。

除了ODBC以外,微软还提出过OleDB, ADO.NET等概念。其实访问数据库性能最佳的方式是每个数据库厂商位某种特定语言提供访问接口。但是这样做交互和匹配成本比较高。如果没有特别的需求,大家还是希望使用通用的接口。所以虽然ODBC不是那么易用,但是依然比较通用。

接下来,按照Working with FileMaker data in Python里面的指导,尝试使用Python作为ODBC访问客户端。需要按照pyodbc库。

但是执行的时候发生了一下问题

>>> import pyodbc
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: dlopen(/Users/macey/Library/Python/3.9/lib/python/site-packages/pyodbc.cpython-39-darwin.so, 2): Library not loaded: /usr/local/opt/unixodbc/lib/libodbc.2.dylib

按照提示应该按照unixodbc。但是实际上,必须使用iodbc才能功能。但是这样的话需要重新编译pyodbc,显得比较麻烦。

关于使用pyodbc的其他参考

JDBC

JDBC是FileMaker提供的另一个接口,顾名思义,是给JVM之上的语言使用的,比如JAVA。所以如果一个基于Java的数据库管理工具支持JDBC的话,就可以通过FileMaker的驱动,对FileMaker的数据库进行操作。

基于Java的常用的免费的数据库大概有DBeaver Community Edition。可是找了半天也不知道如何在DBeaver中自定义连接到一个数据库。只能创建DBeaver中已经支持的数据库的连接。可惜FileMaker不在其列。

另一个选项是采用[SquirrelSQL]

SquirrelSQL界面看上去有点老,但是对于执行SQL来说是不成问题的。要把FileMaker的JDBC驱动放在相应的目录让SquirrelSQL找得到。然后再SquirrelSQL中创建到FileMaker的连接。连接所需要指定的参数在Claris FileMaker ODBC and JDBC Guide中有描述

Wikipedia的Comparison of database tools一文中,[SquirrelSQL]是唯一一款提到对FileMaker支持的产品。

关于FileMaker的Container字段

FileMaker的Container字段既可以保存相关的文件内容,又可以保存到文件的链接。有需要需要把链接转化为真实的文件内容,那么这篇文章From References to Embedded Container Data则提供了一个参考:

#
Allow User Abort [Off]
Set Error Capture [On]
#
#
Set Variable [$tempPath; Value:Get ( TemporaryPath )]
#
#  check if the container is by reference
If [_CONTAINER_storageType ( ContainerData::myContainer ) = _CONTAINER_STORAGE_FILEREFERENCE]
  #
  #  it is, do an export field contents and then insert it again
  #  get the file name
  Set Variable [$containerAsText; Value:ContainerData::myContainer]
  Set Variable [$fileName; Value:_CONTAINER_file_name ( ContainerData::myContainer )]
  #
  #  figure out what file type it is
  Set Variable [$type; Value:LeftWords( $containerAsText ; 1 )]
  If [Right( $fileName ; 4 ) = ".pdf" and $type <> "PDF"]
    Set Variable [$type; Value:"PDF"]
  Else If [$type = "size"]
    #  probably an image, the path is on the 2nd line, first line is the size
    Set Variable [$type; Value:LeftWords( GetValue( $containerAsText ; 2 ) ; 1 )]
  End If
  #
  #  construct the temp path and file name
  Set Variable [$temp; Value:"file:" & $tempPath & $fileName]
  #
  #  export
  Export Field Contents [ContainerData::myContainer; "$temp"]
  #
  #  delete the container and commit
  Set Field [ContainerData::myContainer[]; ""]
  Commit Records/Requests [Skip data entry validation]
  #
  #  now get the container back
  Go to Field [Select/perform; ContainerData::myContainer]
  If [$type = "image"]
    Set Variable [$temp; Value:Substitute ( $temp ; "file:/" ; "image:/" )]
    Insert Picture [$temp]

  Else If [$type = "PDF"]
    Set Variable [$temp; Value:Substitute ( $temp ; "file:/" ; "image:/" )]
    Insert PDF [$temp]

  Else If [$type = "movie"]
    Set Variable [$temp; Value:Substitute ( $temp ; "file:/" ; "movie:/" )]
    Insert Audio/Video [$temp]

  Else
    Insert File [ContainerData::myContainer[]]
  End If
  #
  Commit Records/Requests [Skip data entry validation]
#
End If

其中_CONTAINER_storageType之类的标识是自定义函数,具体的内容则是 GetContainerAttribute ( ContainerData::myContainer ; "storagetype" )

文章中同时也附带了FileMaker示例,有兴趣的可以下载下来学习。

参考

(这里是底线)