質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
EJS

EJSは、JavaScript用のテンプレートエンジン。HTMLなどのテンプレートテキストにJavaScriptのロジックを記述することができます。また、変数・関数の実行をテンプレートテキスト内に埋め込むことも可能です。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Q&A

解決済

1回答

775閲覧

MySQLからPostgreSQLへの移行が上手くいきません。

kanon_2155103

総合スコア7

EJS

EJSは、JavaScript用のテンプレートエンジン。HTMLなどのテンプレートテキストにJavaScriptのロジックを記述することができます。また、変数・関数の実行をテンプレートテキスト内に埋め込むことも可能です。

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

PostgreSQL

PostgreSQLはオープンソースのオブジェクトリレーショナルデータベース管理システムです。 Oracle Databaseで使われるPL/SQLを参考に実装されたビルトイン言語で、Windows、 Mac、Linux、UNIX、MSなどいくつものプラットフォームに対応しています。

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

0グッド

1クリップ

投稿2023/12/01 09:31

編集2023/12/06 11:57

前提

お世話になります。

Node.jsで書かれたメモアプリで、以前はMySQLを接続していたのですが、この度PostgreSQLに移行することになりました。
それに伴い、お尋ねしたい問題がございます。

PostgreSQLと既存のコードが噛み合わない

移行にあたり必要なコードの書き換えなどは一通り終えたつもりなのですが、実行してみたところ、EJSファイルに次のようなエラーが発生してしまいました(EJSというのは、HTML内にJavascriptを簡単に記述できるJSのテンプレートエンジンです)。

app.jsの実行結果

1Listeninng on 8001 2Result { 3 command: 'SELECT', 4 rowCount: 1, 5 oid: null, 6 rows: [ 7 { 8 id: 1, 9 title: 'mikan', 10 content: 'choco', 11 created: 2023-12-01T07:16:14.517Z, 12 modified: 2023-12-01T07:18:47.483Z, 13 is_removed: false, 14 is_locked: false 15 } 16 ], 17 fields: [ 18 Field { 19 name: 'id', 20 tableID: 16399, 21 columnID: 1, 22 dataTypeID: 23, 23 dataTypeSize: 4, 24 dataTypeModifier: -1, 25 format: 'text' 26 }, 27 Field { 28 name: 'title', 29 tableID: 16399, 30 columnID: 2, 31 dataTypeID: 1043, 32 dataTypeSize: -1, 33 dataTypeModifier: 44, 34 format: 'text' 35 }, 36 Field { 37 name: 'content', 38 tableID: 16399, 39 columnID: 3, 40 dataTypeID: 1043, 41 dataTypeSize: -1, 42 dataTypeModifier: 10485764, 43 format: 'text' 44 }, 45 Field { 46 name: 'created', 47 tableID: 16399, 48 columnID: 4, 49 dataTypeID: 1114, 50 dataTypeSize: 8, 51 dataTypeModifier: -1, 52 format: 'text' 53 }, 54 Field { 55 name: 'modified', 56 tableID: 16399, 57 columnID: 5, 58 dataTypeID: 1114, 59 dataTypeSize: 8, 60 dataTypeModifier: -1, 61 format: 'text' 62 }, 63 Field { 64 name: 'is_removed', 65 tableID: 16399, 66 columnID: 6, 67 dataTypeID: 16, 68 dataTypeSize: 1, 69 dataTypeModifier: -1, 70 format: 'text' 71 }, 72 Field { 73 name: 'is_locked', 74 tableID: 16399, 75 columnID: 7, 76 dataTypeID: 16, 77 dataTypeSize: 1, 78 dataTypeModifier: -1, 79 format: 'text' 80 } 81 ], 82 _parsers: [ 83 [Function: parseInteger], 84 [Function: noParse], 85 [Function: noParse], 86 [Function: parseDate], 87 [Function: parseDate], 88 [Function: parseBool], 89 [Function: parseBool] 90 ], 91 _types: TypeOverrides { 92 _types: { 93 getTypeParser: [Function: getTypeParser], 94 setTypeParser: [Function: setTypeParser], 95 arrayParser: [Object], 96 builtins: [Object] 97 RowCtor: null, 98 rowAsArray: false, 99 _prebuiltEmptyResultObject: { 100 >> 31| <% memos.forEach((memo) => { %> 101 32| <li> 102 33| <span class="title-column"><%= memo.title %></span> 103 34| <div class="items"> 104 105memos.forEach is not a function 106 at eval ("C:\\Users\\Kanon\\workspace\\memo-app\\views\\index.ejs":15:14) 107 at index (C:\Users\Kanon\workspace\memo-app\node_modules\ejs\lib\ejs.js:703:17) 108 at tryHandleCache (C:\Users\Kanon\workspace\memo-app\node_modules\ejs\lib\ejs.js:274:36) 109 at View.exports.renderFile [as engine] (C:\Users\Kanon\workspace\memo-app\node_modules\ejs\lib\ejs.js:491:10) 110 at View.render (C:\Users\Kanon\workspace\memo-app\node_modules\express\lib\view.js:135:8) 111 at tryRender (C:\Users\Kanon\workspace\memo-app\node_modules\express\lib\application.js:657:10) 112 at Function.render (C:\Users\Kanon\workspace\memo-app\node_modules\express\lib\application.js:609:3) 113 at ServerResponse.render (C:\Users\Kanon\workspace\memo-app\node_modules\express\lib\response.js:1039:7) 114 at Query.<anonymous> (C:\Users\Kanon\workspace\memo-app\app-new.js:20:8) 115 at Query.handleReadyForQuery (C:\Users\Kanon\workspace\memo-app\node_modules\pg\lib\query.js:139:14)

まだPosrgreSQLに不慣れなため、おそらく書き換えの際に書式の間違いや誤字が発生したのだと思いますが、自力ではこれ以上見つけることができず……。
どうか皆様のお知恵をお貸しくださいませ。

該当のソースコード・テーブル

以下のひとつめが新しく作ったPostgreSQLのmemosテーブルの構造、ふたつめが以前から存在したMySQLのmemosテーブルの構造です。

PostgreSQL

1memo_app=# \d memos; 2 テーブル"public.memos" 3 列 | タイプ | 照合順序 | Null 値を許容 | デフォルト 4------------+-----------------------------+----------+---------------+----------------------------------- 5 id | integer | | not null | nextval('memos_id_seq'::regclass) 6 title | character varying(40) | | | 'Untitled'::character varying 7 content | character varying(10485760) | | | 8 created | timestamp without time zone | | not null | CURRENT_TIMESTAMP 9 modified | > 引用テキストtimestamp without time zone | | not null | CURRENT_TIMESTAMP 10 is_removed | boolean | | not null | false 11 is_locked | boolean | | not null | false 12インデックス: 13 "memos_pkey" PRIMARY KEY, btree (id) 14トリガー: 15 update_tri BEFORE UPDATE ON memos FOR EACH ROW EXECUTE FUNCTION set_update_time()

MySQL

1mysql> desc memos; 2+----------------+----------------+------+-----+-------------------+-----------------------------------------------+ 3| Field | Type | Null | Key | Default | Extra | 4+----------------+----------------+------+-----+-------------------+-----------------------------------------------+ 5| id | int | NO | PRI | NULL | auto_increment | 6| title | varchar(40) | YES | | Untitled | | 7| content | varchar(16383) | YES | | NULL | | 8| created | timestamp | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED | 9| modified | datetime | YES | | CURRENT_TIMESTAMP | DEFAULT_GENERATED on update CURRENT_TIMESTAMP | 10| modified_stamp | varchar(15) | YES | | NULL | STORED GENERATED | 11| is_removed | tinyint(1) | NO | | 0 | | 12| is_locked | tinyint(1) | NO | | 0 | | 13+----------------+----------------+------+-----+-------------------+-----------------------------------------------+ 148 rows in set (0.05 sec)

イタリックテキスト
また、該当のJSファイルとEJSファイルはこちらです。

app.js(/indexと周辺のみ)

1const express = require('express'); 2const mod = require('./module') 3const app = express(); 4 5app.use(express.static('public')); 6app.use(express.urlencoded({extended: false})); 7 8// Memo Listの取得 9app.get('/index', (req, res) => { 10 const connection = mod.connection; 11 connection.query( 12 'SELECT * FROM memos WHERE is_removed = false', 13 (err, data) => { 14 console.log(data.rows); 15 res.render('index.ejs', {memos: data.rows}); 16 connection.end(); 17 } 18 ); 19});

module.js

1const { Client } = require('pg'); 2 3const connection = new Client({ 4 user: 'postgres', 5 host: 'localhost', 6 database: 'hoge', 7 password: 'huga', 8 port: 5432, 9}); 10 11connection.connect(); 12 13module.exports = { 14 connection 15}

index.ejs

1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Kanon's Memo</title> 8 <link rel="icon" href="/images/black-cat.png"> 9 <link rel="stylesheet" href="/css/style.css"> 10</head> 11<body> 12 <%- include('header'); %> 13 <main> 14 <div class="index-container"> 15 <div class="container-header"> 16 <div class="container-title"> 17 <h1>Memo List</h1> 18 <img src="/images/black-cat.png" alt="S-cat" class="cat-image"> 19 </div> 20 <div class="button"> 21 <a href="/trash"class="optional-button move-button">Trash</a> 22 <a href="/new" class="optional-button">+ New</a> 23 </div> 24 </div> 25 <div class="index-table-wrapper"> 26 <div class="table-head"> 27 <span class="title-column">Name</span> 28 <span class="modified-column">Last Modified</span> 29 </div> 30 <ul class="table-body"> 31 <% memos.forEach((memo) => { %> 32 <li> 33 <span class="title-column"><%= memo.title %></span> 34 <div class="items"> 35 <span class="modified-column"><%= memo.modified %></span> 36 <div class="icon-area"> 37 <div class="menu-icon"> 38 <div></div> 39 <div></div> 40 <div></div> 41 </div> 42 <ul class="menu-content"> 43 <li><a href="/edit/<%= memo.id %>">Edit</a></li> 44 <li><form action="/remove/<%= memo.id %>" method="post"> 45 <input type="submit" value="Remove"> 46 </form></li> 47 </ul> 48 </div> 49 </div> 50 </li> 51 <% }); %> 52 </ul> 53 </div> 54 </div> 55 <div class="footer"> 56 <small class="copy-right">&copy; 2023 kanon</small> 57 </div> 58 </main> 59</body> 60</html>

他にご入用の情報があれば仰っていただければ追記いたします。

補足情報(FW/ツールのバージョンなど)

実行環境はWindows11です。

Node: v16.14.2
MySQL: v8.0.3
PostgreSQL: v16.1

どうぞよろしくお願いいたします。

追記

こちらの投稿にて同時に質問していた「PostgreSQLの生成列でTo_charを使いたい」という問題は、本題がふたつ存在したことで分かり辛くなってしまったため、別の投稿に移動いたしました。
https://teratail.com/questions/5xbktfa8l5dr0f

また、こちらの投稿ででいただいたアドバイスを試してみたところ、出力結果に進歩が見られましたので、別の投稿に移動することになりました。続きはこちらをご覧いただければと思います。
https://teratail.com/questions/2k3v2jas7rn82h

お付き合いいただいている皆様にはご不便をおかけしてしまい、誠に申し訳ございません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ikedas

2023/12/01 12:39

コードやメッセージやその他のコンソール出力を画像ではなくテキストとして提示してください。画像だとそれについて言及するのにいちいち打ち直さなければいけませんし、その過程で写し間違いも生じます。
kanon_2155103

2023/12/03 10:00 編集

> ikedas様 コメントありがとうございます! 未熟で申し訳ございません。ご指摘いただいたとおり、画像を全てテキストに差し替えてみました。
hoshi-takanori

2023/12/03 04:42

DB アクセスにはどのライブラリをお使いですか? mysql2 (node-mysql2) と pg (node-postgres) では使い方が大幅に異なる気がしますが…。
kanon_2155103

2023/12/03 10:02 編集

> hoshi-takanori様 コメントありがとうございます。 ライブラリはこの間までmysql2を使っていましたが、この度pgに変更いたしました。上記にコネクションを担っているmodule.jsの情報を追加しましたので、よろしければご参考にしていただければと思います。
ikedas

2023/12/03 07:54

PostgreSQLのテーブルやapp.js中のSQL文ではis_removed , is_lockedという列名が使われていますが、MySQLのテーブルにそういう列はありません。これではSQL文の実行は失敗するので、index.ejsに渡されるmemosの値は空であり、forEach()という関数を実行できるオブジェクトにはならないと思います。
kanon_2155103

2023/12/03 08:01

> ikedas様 ありがとうございます。 MySQLは以前まで使用していたのですが、現在PostgreSQLに移行を試みているため、app.jsもPostgreの方に合わせて変更しています。 ややこしい質問の仕方になっており恐縮です……。
ikedas

2023/12/03 08:05

あ、逆でしたか。ごめんなさい。 とにかく、SQL文の実行が失敗している可能性があるので、エラーメッセージなどが出ていればその全文を記してください。またEJSに渡されているmemosにどのようなものが入っているかわかれば記してください。 いずれも、このコメント欄に書くのではなく、質問文を編集して書いてください。
kanon_2155103

2023/12/03 09:01

> ikedas様 ご返信ありがとうございます。 お待たせしてしまいましたが、実行結果について追記しました。 本文の字数制限を超えてしまったため、Google Driveに実行結果をコピペしたテキストファイルをアップロードいたしました。もし何か問題などがございましたら仰ってくださいませ。
ikedas

2023/12/03 09:13

質問に関係ない処理のコードを削除していって、問題が再現する最小限のコードだけにしてください。 あと、memosに渡された内容がわかれば記してください。
kanon_2155103

2023/12/03 09:56 編集

> ikedas様 お待たせしてすみません! app.jsの/index周辺以外のコードを削除して、実行結果を全て記載しました。 結果の116行目までがconsole.logで出力したテーブル情報で、おそらくこれと同じ内容がmemosに渡されていると思われます(私の知識量では確証が持てませんが……)。
hoshi-takanori

2023/12/03 10:25

> mysql2 (node-mysql2) と pg (node-postgres) では使い方が大幅に異なる と書いたように、mysql2 では memos に渡されるのは rows 配列ですが、pg ではもっと複雑なオブジェクトになってますね。とりあえず、app.js の {memos: data} を {memos: data.rows} にすれば良いかも?
kanon_2155103

2023/12/03 13:47 編集

> hoshi-takanori様、ikedas様 お二方ともありがとうございます。 hoshi-ktanori様の仰るようにdataとなっている部分をdata.rowsに変えてみたところ、実行後に最初にアクセスしたページ(/indexと/trash)を読み込めるようになりました! ただ、そこから別のページに飛ぼうとすると、これまでとは違うエラーが起こるようになりました。 質問本文の出力結果を最新のものに更新し、それに伴い記載するapp.jsの範囲も増やしましたので、ご確認いただけますでしょうか……? 恐れ入りますが、今しばらくお付き合いいただけますと幸いです。
ikedas

2023/12/04 10:09 編集

すみません。前に起きていた問題とその解決状況については残しておいていただけないでしょうか。 こちらは元の質問 (分離したto_charに関する質問を除く) を復元した上で解決処理してはどうですか。 自己解決の手順を参照: https://teratail.com/help#resolve-myself その上で、新しく起きたエラーは可能ならば新しい質問で尋ねることとしたほうが良いように思います。 当サイトでの質問と回答が当事者以外にも公開される理由は、同様の問題に行き当たった人の役に立つかもしれないからです。回答する側としても、そういう意味のある活動でなければ無償で回答したり助言したりはしません。
kanon_2155103

2023/12/04 16:05 編集

> ikedas様 お返事が遅くなりすみません。 仰るとおりですね。質問サイトの本文を見失っておりました。 質問を以前の状態に戻し、その上で新しい質問を投稿し直すことにいたします。新しい質問の投稿は、時間的な都合から後日になるかもしれません。 また、解決処理につきましてはご回答くださった方がおひとりおられますので、その方をベストアンサーにさせていただくことで完了しようと思います。 至らぬ行動で皆様を何度も振り回してしまったこと、心よりお詫び申し上げます。 ここまでお付き合いいただき誠にありがとうございました。
guest

回答1

0

ベストアンサー

node.jsについては全く知らないので、的外れなことかもしれませんのでご容赦下さい。


ふたつめのテーブル作成については、察するにmodifiedカラムの生成に失敗してるのかと思います。
modifiedtimestampなのに、記述では元のvarchar(15)に日付書式で設定しようとしているからでは無いでしょうか。
createdの定義に問題無いのであれば、同様に記述すればよいのではないでしょうか?

ひとつめは、テーブル構造の変更により、index.ejs:31についての差異が生じているように思います。
ふたつめが要因だったりしないでしょうか。


PostgreSQLの生成列でTo_charを使いたい

という事だったんですね。
生成列に関しては、IMMUTABLEで無い関数(to_char())は使用できません。
代替案としては、生成列ではないカラムに変更して、トリガーで更新する事です。

投稿2023/12/02 10:00

編集2023/12/04 00:37
sazi

総合スコア25298

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kanon_2155103

2023/12/03 08:18 編集

ご回答ありがとうございます! ふたつめにいただいた言及につきまして、以下のように変更してみたのですが、以前と同じような結果が出てしまい上手くいきませんでした。 > alter table memos add modified_stamp timestamp default generated always as (to_char(modified, 'Mon DD YY HH24:MI')); 他にも以下の構文を試してみたのですが、結果は変わらず……。 > alter table memos add modified_stamp timestamp generated always as (to_char(modified, 'Mon DD YY HH24:MI')); // defaultを抜いてみました。 > alter table memos add modified_stamp timestamp generated always as identity (to_char(modified, 'Mon DD YY HH24:MI')) stored; // ひとつ上からさらにidentityとstoredを追加しました。
kanon_2155103

2023/12/03 08:17

ひとつめの問題につきましては、いただいたアドバイスを元に今一度考えてみようと思います。
sazi

2023/12/04 00:03 編集

> alter table memos add modified_stamp timestamp default generated always as (to_char(modified, 'Mon DD YY HH24:MI')); Createdと同様な記述をしているんですか? 日付型に書式指定するという意味の分からない指定にしか見えません。 日付型に書式指定するのは、通常取得時に整形するものです。 そのように格納したいのであれば、元の文字列型にする必要があると思います。
kanon_2155103

2023/12/03 15:59

そ、そんなに難しい話だったのですね……。 事情があって取得時に指定するのが難しいため、もう少し粘ってみようと思います。 同じ投稿でふたつの質問を行ったことで見ていただく方の混乱を招いてしまう恐れがあったため、ふたつめの質問を別の投稿に分けることにいたしました。 こちらが至らないばかりにご不便をおかけしたにも関わらず、お付き合いいただきありがとうございました!
sazi

2023/12/04 00:52 編集

> 同じ投稿でふたつの質問を行ったことで見ていただく方の混乱を招いてしまう恐れがあったため、ふたつめの質問を別の投稿に分けることにいたしました。 ひとつめを分けた方が、すっきりしたんでは無いかと思いますが。
kanon_2155103

2023/12/04 15:16

振り回してしまい本当に恐縮です……。 また、移行先の質問にも回答をしてくださってありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問