mirror of
https://github.com/cole-maxwell1/cole-maxwell1.github.io.git
synced 2026-01-25 22:41:47 -05:00
120 lines
46 KiB
HTML
120 lines
46 KiB
HTML
<!DOCTYPE html><html lang="en" ><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><meta name="generator" content="Jekyll v4.2.2" /><meta property="og:title" content="Using ODBC on IBM i for Local Linux Development" /><meta property="og:locale" content="en" /><meta name="description" content="If you are new to the IBM i platform coming right out of school, like me, or you are a developer used to working exclusively with open-source tooling, the IBM i platform can be a strange place. The legacy application support is industry leading, to both the benefit and downside of the platform. Under the hood there is IBM’s powerful and well tested relational database, DB2 for i. So far, I like what I see. Is it possible to get the best of the open-source tooling while leveraging the power of an existing enterprise grade database? My initial take is yes, and the ODBC driver on the platform is the answer." /><meta property="og:description" content="If you are new to the IBM i platform coming right out of school, like me, or you are a developer used to working exclusively with open-source tooling, the IBM i platform can be a strange place. The legacy application support is industry leading, to both the benefit and downside of the platform. Under the hood there is IBM’s powerful and well tested relational database, DB2 for i. So far, I like what I see. Is it possible to get the best of the open-source tooling while leveraging the power of an existing enterprise grade database? My initial take is yes, and the ODBC driver on the platform is the answer." /><link rel="canonical" href="https://colemaxwell.dev/posts/ibmi-odbc-on-linux/" /><meta property="og:url" content="https://colemaxwell.dev/posts/ibmi-odbc-on-linux/" /><meta property="og:site_name" content="Cole Maxwell" /><meta property="og:type" content="article" /><meta property="article:published_time" content="2022-08-03T19:39:00-05:00" /><meta name="twitter:card" content="summary" /><meta property="twitter:title" content="Using ODBC on IBM i for Local Linux Development" /><meta name="twitter:site" content="@twitter_username" /> <script type="application/ld+json"> {"@context":"https://schema.org","@type":"BlogPosting","dateModified":"2022-08-03T21:48:45-05:00","datePublished":"2022-08-03T19:39:00-05:00","description":"If you are new to the IBM i platform coming right out of school, like me, or you are a developer used to working exclusively with open-source tooling, the IBM i platform can be a strange place. The legacy application support is industry leading, to both the benefit and downside of the platform. Under the hood there is IBM’s powerful and well tested relational database, DB2 for i. So far, I like what I see. Is it possible to get the best of the open-source tooling while leveraging the power of an existing enterprise grade database? My initial take is yes, and the ODBC driver on the platform is the answer.","headline":"Using ODBC on IBM i for Local Linux Development","mainEntityOfPage":{"@type":"WebPage","@id":"https://colemaxwell.dev/posts/ibmi-odbc-on-linux/"},"url":"https://colemaxwell.dev/posts/ibmi-odbc-on-linux/"}</script><title>Using ODBC on IBM i for Local Linux Development | Cole Maxwell</title><link rel="apple-touch-icon" sizes="180x180" href="/assets/img/favicons/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/img/favicons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/assets/img/favicons/favicon-16x16.png"><link rel="manifest" href="/assets/img/favicons/site.webmanifest"><link rel="shortcut icon" href="/assets/img/favicons/favicon.ico"><meta name="apple-mobile-web-app-title" content="Cole Maxwell"><meta name="application-name" content="Cole Maxwell"><meta name="msapplication-TileColor" content="#da532c"><meta name="msapplication-config" content="/assets/img/favicons/browserconfig.xml"><meta name="theme-color" content="#ffffff"><link rel="preconnect" href="https://fonts.googleapis.com" ><link rel="dns-prefetch" href="https://fonts.googleapis.com" ><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link rel="dns-prefetch" href="https://fonts.gstatic.com" crossorigin><link rel="preconnect" href="https://fonts.googleapis.com" ><link rel="dns-prefetch" href="https://fonts.googleapis.com" ><link rel="preconnect" href="https://cdn.jsdelivr.net" ><link rel="dns-prefetch" href="https://cdn.jsdelivr.net" ><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Lato&family=Source+Sans+Pro:wght@400;600;700;900&display=swap"><link rel="preconnect" href="https://www.google-analytics.com" crossorigin="use-credentials"><link rel="dns-prefetch" href="https://www.google-analytics.com"><link rel="preconnect" href="https://www.googletagmanager.com" crossorigin="anonymous"><link rel="dns-prefetch" href="https://www.googletagmanager.com"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/css/bootstrap.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.11.2/css/all.min.css"><link rel="stylesheet" href="/assets/css/style.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/afeld/bootstrap-toc@1.0.1/dist/bootstrap-toc.min.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/magnific-popup@1/dist/magnific-popup.min.css"> <script src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script> <script type="text/javascript"> class ModeToggle { static get MODE_KEY() { return "mode"; } static get MODE_ATTR() { return "data-mode"; } static get DARK_MODE() { return "dark"; } static get LIGHT_MODE() { return "light"; } static get ID() { return "mode-toggle"; } constructor() { if (this.hasMode) { if (this.isDarkMode) { if (!this.isSysDarkPrefer) { this.setDark(); } } else { if (this.isSysDarkPrefer) { this.setLight(); } } } let self = this; /* always follow the system prefers */ this.sysDarkPrefers.addEventListener("change", () => { if (self.hasMode) { if (self.isDarkMode) { if (!self.isSysDarkPrefer) { self.setDark(); } } else { if (self.isSysDarkPrefer) { self.setLight(); } } self.clearMode(); } self.notify(); }); } /* constructor() */ get sysDarkPrefers() { return window.matchMedia("(prefers-color-scheme: dark)"); } get isSysDarkPrefer() { return this.sysDarkPrefers.matches; } get isDarkMode() { return this.mode === ModeToggle.DARK_MODE; } get isLightMode() { return this.mode === ModeToggle.LIGHT_MODE; } get hasMode() { return this.mode != null; } get mode() { return sessionStorage.getItem(ModeToggle.MODE_KEY); } /* get the current mode on screen */ get modeStatus() { if (this.isDarkMode || (!this.hasMode && this.isSysDarkPrefer)) { return ModeToggle.DARK_MODE; } else { return ModeToggle.LIGHT_MODE; } } setDark() { $('html').attr(ModeToggle.MODE_ATTR, ModeToggle.DARK_MODE); sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.DARK_MODE); } setLight() { $('html').attr(ModeToggle.MODE_ATTR, ModeToggle.LIGHT_MODE); sessionStorage.setItem(ModeToggle.MODE_KEY, ModeToggle.LIGHT_MODE); } clearMode() { $('html').removeAttr(ModeToggle.MODE_ATTR); sessionStorage.removeItem(ModeToggle.MODE_KEY); } /* Notify another plugins that the theme mode has changed */ notify() { window.postMessage({ direction: ModeToggle.ID, message: this.modeStatus }, "*"); } } /* ModeToggle */ const toggle = new ModeToggle(); function flipMode() { if (toggle.hasMode) { if (toggle.isSysDarkPrefer) { if (toggle.isLightMode) { toggle.clearMode(); } else { toggle.setLight(); } } else { if (toggle.isDarkMode) { toggle.clearMode(); } else { toggle.setDark(); } } } else { if (toggle.isSysDarkPrefer) { toggle.setLight(); } else { toggle.setDark(); } } toggle.notify(); } /* flipMode() */ </script><body data-spy="scroll" data-target="#toc" data-topbar-visible="true"><div id="sidebar" class="d-flex flex-column align-items-end"><div class="profile-wrapper text-center"><div id="avatar"> <a href="/" class="mx-auto"> <img src="https://avatars.githubusercontent.com/u/58858010?v=4" alt="avatar" onerror="this.style.display='none'"> </a></div><div class="site-title mt-3"> <a href="/">Cole Maxwell</a></div><div class="site-subtitle font-italic">Craftsmanship in software and systems</div></div><ul class="w-100"><li class="nav-item"> <a href="/" class="nav-link"> <i class="fa-fw fas fa-home ml-xl-3 mr-xl-3 unloaded"></i> <span>HOME</span> </a><li class="nav-item"> <a href="/categories/" class="nav-link"> <i class="fa-fw fas fa-stream ml-xl-3 mr-xl-3 unloaded"></i> <span>CATEGORIES</span> </a><li class="nav-item"> <a href="/tags/" class="nav-link"> <i class="fa-fw fas fa-tag ml-xl-3 mr-xl-3 unloaded"></i> <span>TAGS</span> </a><li class="nav-item"> <a href="/archives/" class="nav-link"> <i class="fa-fw fas fa-archive ml-xl-3 mr-xl-3 unloaded"></i> <span>ARCHIVES</span> </a><li class="nav-item"> <a href="/about/" class="nav-link"> <i class="fa-fw fas fa-info-circle ml-xl-3 mr-xl-3 unloaded"></i> <span>ABOUT</span> </a></ul><div class="sidebar-bottom mt-auto d-flex flex-wrap justify-content-center align-items-center"> <button class="mode-toggle btn" aria-label="Switch Mode"> <i class="fas fa-adjust"></i> </button> <span class="icon-border"></span> <a href="https://github.com/cole-maxwell1" aria-label="github" target="_blank" rel="noopener"> <i class="fab fa-github"></i> </a> <a href="https://www.linkedin.com/in/cole-n-maxwell" aria-label="linkedin" target="_blank" rel="noopener"> <i class="fab fa-linkedin"></i> </a> <a href=" javascript:location.href = 'mailto:' + ['cole','codecraftsmen.dev'].join('@')" aria-label="email" > <i class="fas fa-envelope"></i> </a> <a href="/feed.xml" aria-label="rss" > <i class="fas fa-rss"></i> </a></div></div><div id="topbar-wrapper"><div id="topbar" class="container d-flex align-items-center justify-content-between h-100 pl-3 pr-3 pl-md-4 pr-md-4"> <span id="breadcrumb"> <span> <a href="/"> Home </a> </span> <span>Using ODBC on IBM i for Local Linux Development</span> </span> <i id="sidebar-trigger" class="fas fa-bars fa-fw"></i><div id="topbar-title"> Post</div><i id="search-trigger" class="fas fa-search fa-fw"></i> <span id="search-wrapper" class="align-items-center"> <i class="fas fa-search fa-fw"></i> <input class="form-control" id="search-input" type="search" aria-label="search" autocomplete="off" placeholder="Search..."> </span> <span id="search-cancel" >Cancel</span></div></div><div id="main-wrapper" class="d-flex justify-content-center"><div id="main" class="container pl-xl-4 pr-xl-4"><div class="row"><div id="core-wrapper" class="col-12 col-lg-11 col-xl-9 pr-xl-4"><div class="post pl-1 pr-1 pl-md-2 pr-md-2"><h1 data-toc-skip>Using ODBC on IBM i for Local Linux Development</h1><div class="post-meta text-muted"> <span> Posted <em class="" data-ts="1659573540" data-df="ll" data-toggle="tooltip" data-placement="bottom"> Aug 3, 2022 </em> </span> <span> Updated <em class="" data-ts="1659581325" data-df="ll" data-toggle="tooltip" data-placement="bottom"> Aug 3, 2022 </em> </span><div class="d-flex justify-content-between"> <span> By <em> <a href="https://github.com/cole-maxwell1">Cole Maxwell</a> </em> </span><div> <span class="readtime" data-toggle="tooltip" data-placement="bottom" title="2280 words"> <em>12 min</em> read</span></div></div></div><div class="post-content"><p>If you are new to the IBM i platform coming right out of school, like me, or you are a developer used to working exclusively with open-source tooling, the IBM i platform can be a strange place. The legacy application support is industry leading, to both the benefit and downside of the platform. Under the hood there is IBM’s powerful and well tested relational database, DB2 for i. So far, I like what I see. Is it possible to get the best of the open-source tooling while leveraging the power of an existing enterprise grade database? My initial take is yes, and the ODBC driver on the platform is the answer.</p><p>The <a href="https://en.wikipedia.org/wiki/Open_Database_Connectivity">Open Database Connectivity</a> (ODBC) standard is an application programming interface (API) for accessing database management systems (DBMS). The designers of ODBC aimed to make it independent of database systems and operating systems. From what I have seen so far IBM is continuing to make improvements to the driver in the latest release of IBM i. I personally view ODBC as the future of the platform and a key factor to help IBM i remain relevant as the years progress.</p><p>As I have gotten my start, the other open-source advocates on the IBM i platform have provided many excellent articles, source code examples, and video lessons that have aided my on boarding to the platform. However, even though the topic of ODBC has been extensively covered I still felt like a comprehensive guide for connecting locally to DB2 for i via ODBC on a linux development machine was missing.</p><p>This post has compiled the good, but scattered, information to explain how an open-source developer would go about connecting up their local linux machine to DB2 for i to develop an application in an open-source language of their choice. I want to give a big thank you and credit to <strong>Liam Allan</strong>, <strong>Kevin Adler</strong>, <strong>Seiden Group</strong>, and <strong>FormaServe</strong> for helping get this information out to the community. They are the heavy hitters aiding the open-source embracement on IBM i. Much of the following is copied directly from their blogs and video resources. Please see the <a href="#references">references</a> at the end of this post.</p><h1 id="prerequisites">Prerequisites</h1><p>You must have the ODBC driver installed on the IBM i you want to connect to. See Seiden Group’s guide<sup id="fnref:Seiden" role="doc-noteref"><a href="#fn:Seiden" class="footnote" rel="footnote">1</a></sup> for <a href="https://www.seidengroup.com/2022/07/11/using-yum-to-install-or-update-the-ibm-i-odbc-driver/">Using YUM to Install or Update the IBM i ODBC Driver</a></p><h3 id="enhance-your-understanding"><span class="mr-2">Enhance Your Understanding</span><a href="#enhance-your-understanding" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h3><p>If your background is exclusively developing on the IBM i (AS400) and you don’t have experience with Linux or other Unix-like operating systems you may want to spend some time reading about the basic file structure of Unix-like operating systems. You will find these patterns in the <code class="language-plaintext highlighter-rouge">/QOpenSys</code> directory on IBM i and it will aid your journey to better understand open-source on IBM i and even Linux.</p><ul><li>University of Cincinnati: <a href="https://homepages.uc.edu/~thomam/Intro_Unix_Text/File_System.html">The Unix File System</a><li>IBM: <a href="https://www.ibm.com/docs/en/i/7.5?topic=systems-open-file-system-qopensys">Open systems file system (QOpenSys)</a></ul><h1 id="installing-the-repository">Installing the Repository</h1><p>IBM has made RPM and DEB package manager repositories for Linux available directly from IBM for the <code class="language-plaintext highlighter-rouge">IBM i Access Client Solutions</code> application package, which includes the <code class="language-plaintext highlighter-rouge">IBM i Access ODBC driver</code>.</p><p>With this change, it is much easier to install the driver on Linux. It also makes it easier for automation to install the driver as well, whether that’s Ansible system deployment scripts or Dockerfiles for building ODBC-based Linux container apps. In addition, it makes updating the driver much easier too, since the process uses the same upgrade procedure as the rest of the system packages<sup id="fnref:Adler" role="doc-noteref"><a href="#fn:Adler" class="footnote" rel="footnote">2</a></sup>.</p><p>The repositories are located under: <a href="https://public.dhe.ibm.com/software/ibmi/products/odbc/">https://public.dhe.ibm.com/software/ibmi/products/odbc/</a>.</p><h2 id="add-the-repository-to-the-package-manager"><span class="mr-2">Add the Repository to the Package Manager</span><a href="#add-the-repository-to-the-package-manager" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h2><p>First, you must add IBM’s package repository to your distribution’s package manager.</p><h3 id="debian-based-and-ubuntu-based-distribution-setup"><span class="mr-2">Debian-based and Ubuntu-based Distribution Setup</span><a href="#debian-based-and-ubuntu-based-distribution-setup" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h3><div class="language-shell highlighter-rouge"><div class="code-header"> <span data-label-text="Shell"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>curl https://public.dhe.ibm.com/software/ibmi/products/odbc/debs/dists/1.1.0/ibmi-acs-1.1.0.list | <span class="nb">sudo tee</span> /etc/apt/sources.list.d/ibmi-acs-1.1.0.list
|
||
</pre></table></code></div></div><h3 id="red-hat-based-distribution-setup"><span class="mr-2">Red Hat-based Distribution Setup</span><a href="#red-hat-based-distribution-setup" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h3><div class="language-shell highlighter-rouge"><div class="code-header"> <span data-label-text="Shell"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>curl https://public.dhe.ibm.com/software/ibmi/products/odbc/rpms/ibmi-acs.repo | <span class="nb">sudo tee</span> /etc/yum.repos.d/ibmi-acs.repo
|
||
</pre></table></code></div></div><h3 id="suse-based-distribution-setup"><span class="mr-2">SUSE-based Distribution Setup</span><a href="#suse-based-distribution-setup" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h3><div class="language-shell highlighter-rouge"><div class="code-header"> <span data-label-text="Shell"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>curl https://public.dhe.ibm.com/software/ibmi/products/odbc/rpms/ibmi-acs.repo | <span class="nb">sudo tee</span> /etc/zypp/repos.d/ibmi-acs.repo
|
||
</pre></table></code></div></div><h1 id="installing-the-odbc-driver">Installing the ODBC driver</h1><p>Now install the package via the distribution’s package manager.</p><h3 id="debian-based-and-ubuntu-based-distribution-installation"><span class="mr-2">Debian-based and Ubuntu-based Distribution Installation</span><a href="#debian-based-and-ubuntu-based-distribution-installation" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h3><div class="language-shell highlighter-rouge"><div class="code-header"> <span data-label-text="Shell"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
2
|
||
</pre><td class="rouge-code"><pre><span class="nb">sudo </span>apt update
|
||
<span class="nb">sudo </span>apt <span class="nb">install </span>ibm-iaccess
|
||
</pre></table></code></div></div><h3 id="red-hat-based-distribution-installation"><span class="mr-2">Red Hat-based Distribution Installation</span><a href="#red-hat-based-distribution-installation" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h3><div class="language-shell highlighter-rouge"><div class="code-header"> <span data-label-text="Shell"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre><span class="nb">sudo </span>dnf <span class="nb">install</span> <span class="nt">--refresh</span> ibm-iaccess
|
||
</pre></table></code></div></div><h3 id="suse-based-distribution-installation"><span class="mr-2">SUSE-based Distribution Installation</span><a href="#suse-based-distribution-installation" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h3><div class="language-shell highlighter-rouge"><div class="code-header"> <span data-label-text="Shell"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
2
|
||
</pre><td class="rouge-code"><pre><span class="nb">sudo </span>zypper refresh
|
||
<span class="nb">sudo </span>zypper <span class="nb">install </span>ibm-iaccess
|
||
</pre></table></code></div></div><h1 id="configuring-the-connection-fyi">Configuring the Connection (FYI)</h1><p>Now that you have the IBM i Access ODBC Driver installed on your system, you are ready to connect to Db2 on i. Below is some background information on the two methods to create a connection.</p><h2 id="connection-strings"><span class="mr-2">Connection Strings</span><a href="#connection-strings" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h2><p>ODBC uses a connection string with keywords to create a database connection. Keywords are case insensitive, and values passed are separated from the keyword by an equals sign (“=”) and end with a semi-colon (“;”). As long as you are using an ODBC database connector, you should be able to pass an identical connection string in any language or technology and be confident that it will correctly connect to Db2 on i. A common connection string may look something like<sup id="fnref:odbc-docs" role="doc-noteref"><a href="#fn:odbc-docs" class="footnote" rel="footnote">3</a></sup>:</p><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>DRIVER=IBM i Access ODBC Driver;SYSTEM=my.ibmi.system;UID=foo;PWD=bar;
|
||
</pre></table></code></div></div><p>In the above example, we define the following connection options:</p><ul><li>DRIVER: The ODBC driver for Db2 for i that we are using to connect to the database (and that we installed above)<li>SYSTEM: The location of your IBM i system, which can be its network name, IP address, or similar<li>UID: The User ID that you want to use on the IBM i system that you are connecting to<li>PWD: The password of the User ID passed above.</ul><p>These are only some of the over 70 connection options you can use when connecting to Db2 on i using the IBM i Access ODBC Driver. A complete list of IBM i Access ODBC Driver connection options can be found at the <a href="https://www.ibm.com/docs/en/i/7.4?topic=details-connection-string-keywords">IBM Knowledge Center: Connection string keywords webpage</a>. If passing connections options through the connection string, be sure to use the keyword labeled with <strong>Connection String</strong>.</p><h2 id="dsns"><span class="mr-2">DSNs</span><a href="#dsns" class="anchor text-muted"><i class="fas fa-hashtag"></i></a></h2><p>As you add more and more options to your connection string, your connection string can become quite cumbersome. Luckily, ODBC offers another way of defining connection options called a DSN (datasource name). Where you define your DSN will depend on whether you are using Windows ODBC driver manager or unixODBC on Linux or IBM i<sup id="fnref:odbc-docs:1" role="doc-noteref"><a href="#fn:odbc-docs" class="footnote" rel="footnote">3</a></sup>.</p><p>IBM i, Linux distributions, and macOS use unixODBC and have nearly identical methods of setting up your drivers and your DSNs.</p><p><code class="language-plaintext highlighter-rouge">odbc.ini</code> and <code class="language-plaintext highlighter-rouge">.odbc.ini</code></p><p>When using unixODBC, DSNs are defined in <code class="language-plaintext highlighter-rouge">odbc.ini</code> and <code class="language-plaintext highlighter-rouge">.odbc.ini</code> (note the <code class="language-plaintext highlighter-rouge">.</code> preceding the latter). These two files have the same structure, but have one important difference:</p><p><code class="language-plaintext highlighter-rouge">odbc.ini</code> defines DSNs that are available to all users on the system. If there are DSNs that should be available to everyone, they can be defined and shared here. Likely, this file is located in the default location, which depends on whether you are on IBM i or Linux:</p><p>IBM i: <code class="language-plaintext highlighter-rouge">/QOpenSys/etc/odbc.ini</code></p><p>Linux: <code class="language-plaintext highlighter-rouge">/etc/unixODBC/odbc.ini</code></p><p>If you want to make sure, the file can be found by running:</p><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>odbcinst -j
|
||
</pre></table></code></div></div><p>.odbc.ini is found in your home directory <code class="language-plaintext highlighter-rouge">~/</code> and defines DSNs that are available only to you. If you are going to define DSNs with your personal username and password, this is the place to do it.</p><p>In both <code class="language-plaintext highlighter-rouge">odbc.ini</code> and <code class="language-plaintext highlighter-rouge">.odbc.ini</code>, you name your DSN with <code class="language-plaintext highlighter-rouge">[]</code> brackets, then specify keywords and values below it. An example of a DSN stored in <code class="language-plaintext highlighter-rouge">~/.odbc.ini</code> used to connect to an IBM i system with private credentials might look like:</p><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
</pre><td class="rouge-code"><pre>[MYDSN]
|
||
Description = My IBM i System
|
||
Driver = IBM i Access ODBC Driver
|
||
System = my.ibmi.system
|
||
UserID = foo
|
||
Password = bar
|
||
Naming = 0
|
||
DefaultLibraries = MYLIB
|
||
TrueAutoCommit = 1
|
||
</pre></table></code></div></div><p>In the above example, we define the following connection options:</p><ul><li>Driver: The ODBC driver for Db2 for i that we are using to connect to the database (and that we installed above)<li>System: The location of your IBM i system, which can be its network name, IP address, or similar<li>UserID: The User ID that you want to use on the IBM i system that you are connecting to<li>Password: The password of the User ID passed above.<li>Naming: Specifies the naming convention used when referring to tables. For more information, refer to Naming <a href="https://www.ibm.com/docs/en/ssw_ibm_i_74/db2/rbafzch2nam.htm">conventions</a> in the DB2 for i SQL reference.<li>DefaultLibraries: Specifies the IBM i libraries to add to the server job’s library list as well as the default library used to resolve unqualified names. The libraries can be delimited by commas or spaces.<li>TrueAutoCommit: Specifies how to handle autocommit support</ul><p>Like connection string keywords, DSN keywords can be found at the <a href="https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/rzaik/connectkeywords.htm">IBM Knowledge Center: Connection string keywords webpage</a>. When passing connection options through a DSN, be sure to use the keyword labeled with ODBC.INI.</p><h1 id="user-level-connection">User Level Connection</h1><ol><li>In your home directory create a <code class="language-plaintext highlighter-rouge">.odbc.ini</code> file</ol><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>touch .odbc.ini
|
||
</pre></table></code></div></div><ol><li>Using your text editor of choice (<a href="https://www.redhat.com/sysadmin/introduction-vi-editor">vi</a>, <a href="https://linuxfoundation.org/blog/classic-sysadmin-vim-101-a-beginners-guide-to-vim/">vim</a>, <a href="https://www.howtogeek.com/howto/42980/the-beginners-guide-to-nano-the-linux-command-line-text-editor/">nano</a>, ect…) add the following DSN configuration, changing the <code class="language-plaintext highlighter-rouge">UserID</code> and <code class="language-plaintext highlighter-rouge">Password</code> to <strong>your</strong> IBM i username and password.</ol><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
</pre><td class="rouge-code"><pre>[devserver]
|
||
Description = Connection to development power server
|
||
Driver = IBM i Access ODBC Driver
|
||
System = devserver.mycompany.com
|
||
UserID = myUsername
|
||
Password = myPassword
|
||
Naming = 0
|
||
DefaultLibraries = businessDataLib,myUserLib,myBusinessUtils
|
||
</pre></table></code></div></div><p>In the above example, we define the following connection options:</p><ul><li>Driver: The ODBC driver for Db2 for i that we are using to connect to the database (This must be installed on the OS)<li>System: The location of your IBM i system, which can be its network name, IP address, or similar<li>UserID: The User ID that you want to use on the IBM i system that you are connecting to<li>Password: The password of the User ID passed above.<li>Naming: Specifies the naming convention used when referring to tables. A <code class="language-plaintext highlighter-rouge">0</code> indicates you want SQLs naming convention, which is likely what you want for running queries in any program you write.<li>DefaultLibraries: Specifies the IBM i libraries to add to the server job’s library list as well as the default library used to resolve unqualified names. The libraries can be delimited by commas or spaces.</ul><h1 id="testing-the-connection">Testing the Connection</h1><p>The <code class="language-plaintext highlighter-rouge">isql</code> command is included with the open source components of unixODBC and unixODBC-devel. You can use this command to run SQL queries via ODBC right from the command-line. To make the connection to the <strong>DSN</strong> configured above pass in the DSN name:</p><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>isql devserver
|
||
</pre></table></code></div></div><p>If the connection is configured correctly, you will get a prompt that looks like this:</p><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
</pre><td class="rouge-code"><pre>+---------------------------------------+
|
||
| Connected! |
|
||
| |
|
||
| sql-statement |
|
||
| help [tablename] |
|
||
| quit |
|
||
| |
|
||
+---------------------------------------+
|
||
SQL>
|
||
</pre></table></code></div></div><p>There is an included sample customer table (<code class="language-plaintext highlighter-rouge">qiws.qcustcdt</code>) on all IBM i systems that can be used for testing<sup id="fnref:FormaServe" role="doc-noteref"><a href="#fn:FormaServe" class="footnote" rel="footnote">4</a></sup></p><p>Type this command into the <code class="language-plaintext highlighter-rouge">SQL></code> prompt:</p><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
</pre><td class="rouge-code"><pre>select * from qiws.qcustcdt
|
||
</pre></table></code></div></div><p>The results will look like this:</p><div class="language-plaintext highlighter-rouge"><div class="code-header"> <span data-label-text="Plaintext"><i class="fas fa-code small"></i></span> <button aria-label="copy" data-title-succeed="Copied!"><i class="far fa-clipboard"></i></button></div><div class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18
|
||
19
|
||
20
|
||
</pre><td class="rouge-code"><pre>SQL> select * from qiws.qcustcdt
|
||
+---------+---------+-----+--------------+-------+------+--------+-------+-------+---------+---------+
|
||
| CUSNUM | LSTNAM | INIT| STREET | CITY | STATE| ZIPCOD | CDTLMT| CHGCOD| BALDUE | CDTDUE |
|
||
+---------+---------+-----+--------------+-------+------+--------+-------+-------+---------+---------+
|
||
| 938472 | Henning | G K | 4859 Elm Ave | Dallas| TX | 75217 | 5000 | 3 | 37.00 | 0 |
|
||
| 839283 | Jones | B D | 21B NW 135 St| Clay | NY | 13041 | 400 | 1 | 100.00 | 0 |
|
||
| 392859 | Vine | S S | PO Box 79 | Broton| VT | 5046 | 700 | 1 | 439.00 | 0 |
|
||
| 938485 | Johnson | J A | 3 Alpine Way | Helen | GA | 30545 | 9999 | 2 | 3987.50 | 33.50 |
|
||
| 397267 | Tyron | W E | 13 Myrtle Dr | Hector| NY | 14841 | 1000 | 1 | 0 | 0 |
|
||
| 389572 | Stevens | K L | 208 Snow Pass| Denver| CO | 80226 | 400 | 1 | 58.75 | 1.50 |
|
||
| 846283 | Alison | J S | 787 Lake Dr | Isle | MN | 56342 | 5000 | 3 | 10.00 | 0 |
|
||
| 475938 | Doe | J W | 59 Archer Rd | Sutter| CA | 95685 | 700 | 2 | 250.00 | 100.00 |
|
||
| 693829 | Thomas | A N | 3 Dove Circle| Casper| WY | 82609 | 9999 | 2 | 0 | 0 |
|
||
| 593029 | Williams| E D | 485 SE 2 Ave | Dallas| TX | 75218 | 200 | 1 | 25.00 | 0 |
|
||
| 192837 | Lee | F L | 5963 Oak St | Hector| NY | 14841 | 700 | 2 | 489.50 | .50 |
|
||
| 583990 | Abraham | M T | 392 Mill St | Isle | MN | 56342 | 9999 | 3 | 500.00 | 0 |
|
||
+---------+---------+-----+--------------+-------+------+--------+-------+-------+---------+---------+
|
||
SQLRowCount returns -1
|
||
12 rows fetched
|
||
SQL>
|
||
</pre></table></code></div></div><p>To exit the prompt press <kbd>Ctr + C</kbd> or type <code class="language-plaintext highlighter-rouge">quit</code> into the prompt</p><h1 id="next-steps">Next Steps</h1><p>Now that you have the ODBC driver installed and configured on you development machine you can use the ODBC drivers in many of your favorite open-source programing languages. See IBM’s <a href="https://www.ibm.com/products/db2-database/developers?utm_content=SRCWW&p1=Search&p4=43700068101138376&p5=p&gclid=Cj0KCQjwuaiXBhCCARIsAKZLt3npUFCKyE-dxmuikHbggZepKbmypy53W5utWVdqpc5xsTI1Jqk_FHQaAusYEALw_wcB&gclsrc=aw.ds">Db2 for Developers</a> page for supported languages.</p><p>Examples of connections in programing languages:</p><ul><li>Seiden Group: <a href="https://www.seidengroup.com/2022/04/15/how-to-query-ibm-i-data-with-php-and-pdo_odbc/">HOW TO QUERY IBM i DATA WITH PHP AND PDO_ODBC</a><li>Liam Allan: <a href="https://worksofbarry.com/?post=25">Using node-odbc on Windows to talk to IBM i</a></ul><h1 id="other-helpful-articles">Other Helpful Articles:</h1><ul><li>Seiden Group: <a href="https://www.seidengroup.com/2020/08/28/qa-ibm-i-odbc-driver/">Q&A: IBM i ODBC DRIVER</a><li>Seiden Group: <a href="https://www.seidengroup.com/2022/05/18/odbc-connection-strings-for-ibm-i-db2/">ODBC CONNECTION STRINGS FOR IBM i DB2</a></ul><h1 id="references">References</h1><div class="footnotes" role="doc-endnotes"><ol><li id="fn:Seiden" role="doc-endnote"><p>Seiden Group: <a href="https://www.seidengroup.com/2022/07/11/using-yum-to-install-or-update-the-ibm-i-odbc-driver/">USING YUM TO INSTALL OR UPDATE THE IBM i ODBC DRIVER</a> <a href="#fnref:Seiden" class="reversefootnote" role="doc-backlink">↩</a></p><li id="fn:Adler" role="doc-endnote"><p>Kevin Adler: <a href="https://kadler.io/2022/05/20/odbc-repos.html">IBM-provided Repositories ODBC Linux Driver Repositories</a> <a href="#fnref:Adler" class="reversefootnote" role="doc-backlink">↩</a></p><li id="fn:odbc-docs" role="doc-endnote"><p><a href="https://ibmi-oss-docs.readthedocs.io/en/latest/odbc/using.html">IBM i OSS Docs</a> <a href="#fnref:odbc-docs" class="reversefootnote" role="doc-backlink">↩</a> <a href="#fnref:odbc-docs:1" class="reversefootnote" role="doc-backlink">↩<sup>2</sup></a></p><li id="fn:FormaServe" role="doc-endnote"><p>FormaServe: <a href="https://www.youtube.com/watch?v=hOFZf4bd_wM">How to use ODBC on the IBM i</a> <a href="#fnref:FormaServe" class="reversefootnote" role="doc-backlink">↩</a></p></ol></div></div><div class="post-tail-wrapper text-muted"><div class="post-meta mb-3"> <i class="far fa-folder-open fa-fw mr-1"></i> <a href='/categories/ibm-i/'>IBM i</a></div><div class="post-tags"> <i class="fa fa-tags fa-fw mr-1"></i> <a href="/tags/ibmi/" class="post-tag no-text-decoration" >ibmi</a> <a href="/tags/odbc/" class="post-tag no-text-decoration" >odbc</a> <a href="/tags/linux/" class="post-tag no-text-decoration" >linux</a> <a href="/tags/as400/" class="post-tag no-text-decoration" >as400</a> <a href="/tags/sql/" class="post-tag no-text-decoration" >sql</a></div><div class="post-tail-bottom d-flex justify-content-between align-items-center mt-3 pt-5 pb-2"><div class="license-wrapper"> This post is licensed under <a href="https://creativecommons.org/licenses/by/4.0/"> CC BY 4.0 </a> by the author.</div><div class="share-wrapper"> <span class="share-label text-muted mr-1">Share</span> <span class="share-icons"> <a href="https://twitter.com/intent/tweet?text=Using+ODBC+on+IBM+i+for+Local+Linux+Development+-+Cole+Maxwell&url=https%3A%2F%2Fcolemaxwell.dev%2Fposts%2Fibmi-odbc-on-linux%2F" data-toggle="tooltip" data-placement="top" title="Twitter" target="_blank" rel="noopener" aria-label="Twitter"> <i class="fa-fw fab fa-twitter"></i> </a> <a href="https://www.facebook.com/sharer/sharer.php?title=Using+ODBC+on+IBM+i+for+Local+Linux+Development+-+Cole+Maxwell&u=https%3A%2F%2Fcolemaxwell.dev%2Fposts%2Fibmi-odbc-on-linux%2F" data-toggle="tooltip" data-placement="top" title="Facebook" target="_blank" rel="noopener" aria-label="Facebook"> <i class="fa-fw fab fa-facebook-square"></i> </a> <a href="https://t.me/share/url?url=https%3A%2F%2Fcolemaxwell.dev%2Fposts%2Fibmi-odbc-on-linux%2F&text=Using+ODBC+on+IBM+i+for+Local+Linux+Development+-+Cole+Maxwell" data-toggle="tooltip" data-placement="top" title="Telegram" target="_blank" rel="noopener" aria-label="Telegram"> <i class="fa-fw fab fa-telegram"></i> </a> <i id="copy-link" class="fa-fw fas fa-link small" data-toggle="tooltip" data-placement="top" title="Copy link" data-title-succeed="Link copied successfully!"> </i> </span></div></div></div></div></div><div id="panel-wrapper" class="col-xl-3 pl-2 text-muted"><div class="access"><div id="access-lastmod" class="post"><div class="panel-heading">Recently Updated</div><ul class="post-content pl-0 pb-1 ml-1 mt-2"><li><a href="/posts/ibmi-odbc-on-linux/">Using ODBC on IBM i for Local Linux Development</a><li><a href="/posts/rest-api-ibmi-qsys2/">Utilize a RESTful API with IBMi QSYS_HTTP Tools in SQL</a></ul></div><div id="access-tags"><div class="panel-heading">Trending Tags</div><div class="d-flex flex-wrap mt-3 mb-1 mr-3"> <a class="post-tag" href="/tags/ibmi/">ibmi</a> <a class="post-tag" href="/tags/sql/">sql</a> <a class="post-tag" href="/tags/api/">api</a> <a class="post-tag" href="/tags/as400/">as400</a> <a class="post-tag" href="/tags/linux/">linux</a> <a class="post-tag" href="/tags/odbc/">odbc</a> <a class="post-tag" href="/tags/qsys2/">qsys2</a> <a class="post-tag" href="/tags/rest/">rest</a></div></div></div><script src="https://cdn.jsdelivr.net/gh/afeld/bootstrap-toc@1.0.1/dist/bootstrap-toc.min.js"></script><div id="toc-wrapper" class="pl-0 pr-4 mb-5"><div class="panel-heading pl-3 pt-2 mb-2">Contents</div><nav id="toc" data-toggle="toc"></nav></div></div></div><div class="row"><div id="tail-wrapper" class="col-12 col-lg-11 col-xl-9 pl-3 pr-3 pr-xl-4"><div id="related-posts" class="mt-5 mb-2 mb-sm-4"><h3 class="pt-2 mt-1 mb-4 ml-1" data-toc-skip>Further Reading</h3><div class="card-deck mb-4"><div class="card"> <a href="/posts/rest-api-ibmi-qsys2/"><div class="card-body"> <em class="small" data-ts="1657830655" data-df="ll" > Jul 14, 2022 </em><h3 class="pt-0 mt-1 mb-3" data-toc-skip>Utilize a RESTful API with IBMi QSYS_HTTP Tools in SQL</h3><div class="text-muted small"><p> The Api This demo used the fakeStoreApi which is a free online REST API that you can use whenever you need Pseudo-real data for without running any server-side code. It’s awesome for teaching purp...</p></div></div></a></div></div></div><div class="post-navigation d-flex justify-content-between"> <a href="/posts/rest-api-ibmi-qsys2/" class="btn btn-outline-primary" prompt="Older"><p>Utilize a RESTful API with IBMi QSYS_HTTP Tools in SQL</p></a><div class="btn btn-outline-primary disabled" prompt="Newer"><p>-</p></div></div></div></div><footer class="row pl-3 pr-3"><div class="col-12 d-flex justify-content-between align-items-center text-muted pl-0 pr-0"><div class="footer-left"><p class="mb-0"> © 2023 <a href="https://github.com/cole-maxwell1">Cole Maxwell</a>. <span data-toggle="tooltip" data-placement="top" title="Except where otherwise noted, the blog posts on this site are licensed under the Creative Commons Attribution 4.0 International (CC BY 4.0) License by the author.">Some rights reserved.</span></p></div><div class="footer-right"><p class="mb-0"> Powered by <a href="https://jekyllrb.com" target="_blank" rel="noopener">Jekyll</a> with <a href="https://github.com/cotes2020/jekyll-theme-chirpy" target="_blank" rel="noopener">Chirpy</a> theme.</p></div></div></footer></div><div id="search-result-wrapper" class="d-flex justify-content-center unloaded"><div class="col-12 col-sm-11 post-content"><div id="search-hints"><div id="access-tags"><div class="panel-heading">Trending Tags</div><div class="d-flex flex-wrap mt-3 mb-1 mr-3"> <a class="post-tag" href="/tags/ibmi/">ibmi</a> <a class="post-tag" href="/tags/sql/">sql</a> <a class="post-tag" href="/tags/api/">api</a> <a class="post-tag" href="/tags/as400/">as400</a> <a class="post-tag" href="/tags/linux/">linux</a> <a class="post-tag" href="/tags/odbc/">odbc</a> <a class="post-tag" href="/tags/qsys2/">qsys2</a> <a class="post-tag" href="/tags/rest/">rest</a></div></div></div><div id="search-results" class="d-flex flex-wrap justify-content-center text-muted mt-3"></div></div></div></div><div id="mask"></div><a id="back-to-top" href="#" aria-label="back-to-top" class="btn btn-lg btn-box-shadow" role="button"> <i class="fas fa-angle-up"></i> </a><div id="notification" class="toast" role="alert" aria-live="assertive" aria-atomic="true" data-animation="true" data-autohide="false"><div class="toast-header"> <button type="button" class="ml-2 ml-auto close" data-dismiss="toast" aria-label="Close"> <span aria-hidden="true">×</span> </button></div><div class="toast-body text-center pt-0"><p class="pl-2 pr-2 mb-3">A new version of content is available.</p><button type="button" class="btn btn-primary" aria-label="Update"> Update </button></div></div><script src="https://cdn.jsdelivr.net/npm/simple-jekyll-search@1.10.0/dest/simple-jekyll-search.min.js"></script> <script> SimpleJekyllSearch({ searchInput: document.getElementById('search-input'), resultsContainer: document.getElementById('search-results'), json: '/assets/js/data/search.json', searchResultTemplate: '<div class="pl-1 pr-1 pl-sm-2 pr-sm-2 pl-lg-4 pr-lg-4 pl-xl-0 pr-xl-0"> <a href="{url}">{title}</a><div class="post-meta d-flex flex-column flex-sm-row text-muted mt-1 mb-1"> {categories} {tags}</div><p>{snippet}</p></div>', noResultsText: '<p class="mt-5">Oops! No results found.</p>', templateMiddleware: function(prop, value, template) { if (prop === 'categories') { if (value === '') { return `${value}`; } else { return `<div class="mr-sm-4"><i class="far fa-folder fa-fw"></i>${value}</div>`; } } if (prop === 'tags') { if (value === '') { return `${value}`; } else { return `<div><i class="fa fa-tag fa-fw"></i>${value}</div>`; } } } }); </script> <script src="https://cdn.jsdelivr.net/combine/npm/magnific-popup@1/dist/jquery.magnific-popup.min.js,npm/lozad/dist/lozad.min.js,npm/clipboard@2/dist/clipboard.min.js"></script> <script src="https://cdn.jsdelivr.net/combine/npm/dayjs@1/dayjs.min.js,npm/dayjs@1/locale/en.min.js,npm/dayjs@1/plugin/relativeTime.min.js,npm/dayjs@1/plugin/localizedFormat.min.js"></script> <script defer src="/assets/js/dist/post.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/js/bootstrap.bundle.min.js"></script> <script defer src="/app.js"></script> <script defer src="https://www.googletagmanager.com/gtag/js?id=G-T9FVDLVRR3"></script> <script> document.addEventListener("DOMContentLoaded", function(event) { window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-T9FVDLVRR3'); }); </script>
|