mm_sql 1.4.0 → 1.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # mm_sql
2
2
 
3
+ [中文](./README.md) | [English](./README_EN.md)
4
+
3
5
  一个通用的SQL帮助类,支持通过切换db_type实现对不同数据库的操作,包括MySQL和SQLite等,提供统一的API接口,支持异步操作和基础SQL执行。
4
6
 
5
7
  ## 安装
@@ -139,6 +141,10 @@ const filteredParams = sql.filter({ id: 1, name: '张三', password: '123' }, ['
139
141
  // 获取底层数据库适配器
140
142
  const dbAdapter = sql.db();
141
143
  // 可用于直接操作底层数据库接口
144
+ db.table = "user_account";
145
+ const user = await db.get({
146
+ user_id: 1
147
+ });
142
148
  ```
143
149
 
144
150
  ### 4. 配置选项
@@ -0,0 +1,162 @@
1
+ const { mysqlAdmin } = require('../mm_mysql');
2
+
3
+ /**
4
+ * 测试run方法对不存在的表的处理
5
+ * @param {Object} admin_mysql - mysqlAdmin实例
6
+ * @param {Object} direct_mysql - 直接Mysql实例
7
+ */
8
+ async function testRunMethod(admin_mysql, direct_mysql) {
9
+ console.log('\n测试1: 比较对不存在的表的处理...');
10
+
11
+ // mysqlAdmin实例测试
12
+ console.log('mysqlAdmin实例测试:');
13
+ try {
14
+ const result1 = await admin_mysql.run('SELECT * FROM non_existent_table_xyz');
15
+ console.log(' ❌ mysqlAdmin没有抛出错误,返回结果:', typeof result1, Array.isArray(result1) ? result1.length : 'N/A');
16
+ } catch (error) {
17
+ console.log(' ✅ mysqlAdmin正确抛出错误:', error.message);
18
+ }
19
+
20
+ // 直接Mysql实例测试
21
+ console.log('直接Mysql实例测试:');
22
+ try {
23
+ const result2 = await direct_mysql.run('SELECT * FROM non_existent_table_xyz');
24
+ console.log(' ❌ 直接Mysql没有抛出错误,返回结果:', typeof result2, Array.isArray(result2) ? result2.length : 'N/A');
25
+ } catch (error) {
26
+ console.log(' ✅ 直接Mysql正确抛出错误:', error.message);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * 比较实例类型和属性
32
+ * @param {Object} admin_mysql - mysqlAdmin实例
33
+ * @param {Object} direct_mysql - 直接Mysql实例
34
+ */
35
+ function compareInstanceType(admin_mysql, direct_mysql) {
36
+ console.log('\n测试2: 比较实例类型和属性...');
37
+ console.log('mysqlAdmin实例类型:', admin_mysql.constructor.name);
38
+ console.log('直接Mysql实例类型:', direct_mysql.constructor.name);
39
+ console.log('mysqlAdmin实例 === 直接Mysql实例:', admin_mysql === direct_mysql);
40
+ console.log('mysqlAdmin实例类型 === 直接Mysql实例类型:', admin_mysql.constructor.name === direct_mysql.constructor.name);
41
+ }
42
+
43
+ /**
44
+ * 检查缓存问题
45
+ * @param {Object} config - 数据库配置
46
+ * @param {Object} admin_mysql - mysqlAdmin实例
47
+ */
48
+ async function checkCache(config, admin_mysql) {
49
+ console.log('\n测试3: 检查缓存问题...');
50
+ const admin_mysql2 = mysqlAdmin('compare_test', config); // 相同scope
51
+ const admin_mysql3 = mysqlAdmin('compare_test2', config); // 不同scope
52
+
53
+ console.log('相同scope实例是否相同:', admin_mysql === admin_mysql2);
54
+ console.log('不同scope实例是否相同:', admin_mysql === admin_mysql3);
55
+
56
+ // 测试不同scope实例的错误处理
57
+ console.log('不同scope实例测试:');
58
+ try {
59
+ const result3 = await admin_mysql3.run('SELECT * FROM non_existent_table_xyz');
60
+ console.log(' ❌ 不同scope实例没有抛出错误,返回结果:', typeof result3, Array.isArray(result3) ? result3.length : 'N/A');
61
+ } catch (error) {
62
+ console.log(' ✅ 不同scope实例正确抛出错误:', error.message);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 比较mysqlAdmin和直接Mysql实例的错误处理行为
68
+ */
69
+ async function compareAdminDirect() {
70
+ console.log('=== 比较mysqlAdmin和直接Mysql实例的错误处理行为 ===');
71
+
72
+ const config = {
73
+ host: 'localhost',
74
+ port: 3306,
75
+ user: 'root',
76
+ password: 'Asd159357',
77
+ database: 'mm'
78
+ };
79
+
80
+ try {
81
+ // 创建mysqlAdmin实例
82
+ const admin_mysql = mysqlAdmin('compare_test', config);
83
+ console.log('✅ mysqlAdmin实例创建成功');
84
+
85
+ // 创建直接Mysql实例
86
+ const { Mysql } = require('../mm_mysql');
87
+ const direct_mysql = new Mysql(config);
88
+ console.log('✅ 直接Mysql实例创建成功');
89
+
90
+ await testRunMethod(admin_mysql, direct_mysql);
91
+ compareInstanceType(admin_mysql, direct_mysql);
92
+ await checkCache(config, admin_mysql);
93
+
94
+ } catch (error) {
95
+ console.log('❌ 实例创建失败:', error.message);
96
+ }
97
+ }
98
+
99
+ /**
100
+ * 测试mm_sql模块中的适配器行为
101
+ */
102
+ async function checkSqlAdapter() {
103
+ console.log('\n=== 测试mm_sql模块中的适配器行为 ===');
104
+
105
+ try {
106
+ const { Sql } = require('./index.js');
107
+
108
+ // 创建mm_sql的MySQL实例
109
+ const mysql_sql = new Sql({
110
+ db_type: 'mysql',
111
+ dir: './db',
112
+ host: 'localhost',
113
+ user: 'root',
114
+ password: 'Asd159357',
115
+ database: 'mm'
116
+ });
117
+
118
+ console.log('✅ mm_sql MySQL实例创建成功');
119
+ console.log('适配器类型:', mysql_sql._adapter?.constructor?.name);
120
+
121
+ // 检查适配器是否与mysqlAdmin返回的实例相同
122
+ const admin_mysql = mysqlAdmin('mm_sql_test', {
123
+ host: 'localhost',
124
+ port: 3306,
125
+ user: 'root',
126
+ password: 'Asd159357',
127
+ database: 'mm'
128
+ });
129
+
130
+ console.log('mm_sql适配器 === mysqlAdmin实例:', mysql_sql._adapter === admin_mysql);
131
+ console.log('mm_sql适配器类型 === mysqlAdmin实例类型:', mysql_sql._adapter?.constructor?.name === admin_mysql.constructor.name);
132
+
133
+ // 测试mm_sql适配器的错误处理
134
+ console.log('\n测试mm_sql适配器的错误处理:');
135
+ try {
136
+ const result = await mysql_sql._adapter.run('SELECT * FROM non_existent_table_xyz');
137
+ console.log(' ❌ mm_sql适配器没有抛出错误,返回结果:', typeof result, Array.isArray(result) ? result.length : 'N/A');
138
+ } catch (error) {
139
+ console.log(' ✅ mm_sql适配器正确抛出错误:', error.message);
140
+ }
141
+
142
+ } catch (error) {
143
+ console.log('❌ mm_sql实例创建失败:', error.message);
144
+ }
145
+ }
146
+
147
+ /**
148
+ * 主测试函数
149
+ */
150
+ async function main() {
151
+ console.log('开始比较mysqlAdmin和直接Mysql实例的行为...\n');
152
+
153
+ await compareAdminDirect();
154
+ await checkSqlAdapter();
155
+
156
+ console.log('\n比较测试完成');
157
+ }
158
+
159
+ // 运行测试
160
+ if (require.main === module) {
161
+ main().catch(console.error);
162
+ }
@@ -0,0 +1,320 @@
1
+ const { Sql } = require('./index.js');
2
+
3
+ /**
4
+ * MySQL和SQLite兼容性测试类
5
+ * @class SqlCompatibilityTest
6
+ */
7
+ class SqlCompatTest {
8
+ /**
9
+ * 构造函数
10
+ */
11
+ constructor() {
12
+ this.passed = 0;
13
+ this.failed = 0;
14
+ this.total = 0;
15
+ this.mysqlSql = null;
16
+ this.sqliteSql = null;
17
+ }
18
+
19
+ /**
20
+ * 断言方法
21
+ * @param {boolean} cond - 断言条件
22
+ * @param {string} msg - 断言消息
23
+ */
24
+ assert(cond, msg) {
25
+ this.total++;
26
+ if (cond) {
27
+ this.passed++;
28
+ console.log(`✅ ${msg}`);
29
+ } else {
30
+ this.failed++;
31
+ console.log(`❌ ${msg}`);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * 初始化MySQL和SQLite实例
37
+ * @returns {Promise<boolean>} 初始化是否成功
38
+ */
39
+ async init() {
40
+ try {
41
+ // 初始化MySQL实例
42
+ this.mysqlSql = new Sql({
43
+ db_type: 'mysql',
44
+ dir: './db',
45
+ host: 'localhost',
46
+ user: 'root',
47
+ password: 'Asd159357',
48
+ database: 'mm'
49
+ });
50
+
51
+ // 初始化SQLite实例
52
+ this.sqliteSql = new Sql({
53
+ db_type: 'sqlite',
54
+ dir: './db',
55
+ database: 'mm'
56
+ });
57
+
58
+ console.log('✅ MySQL和SQLite实例初始化成功');
59
+ return true;
60
+ } catch (error) {
61
+ console.error('❌ 初始化失败:', error.message);
62
+ return false;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 检查配置兼容性
68
+ */
69
+ checkConfigCompat() {
70
+ console.log('\n=== 测试配置方法兼容性 ===');
71
+
72
+ // 测试setConfig方法
73
+ this.mysqlSql.setConfig({ size: 50 });
74
+ this.sqliteSql.setConfig({ size: 50 });
75
+ this.assert(this.mysqlSql.config.size === 50, 'MySQL setConfig方法正常');
76
+ this.assert(this.sqliteSql.config.size === 50, 'SQLite setConfig方法正常');
77
+
78
+ // 测试config属性访问
79
+ this.assert(typeof this.mysqlSql.config === 'object', 'MySQL config属性可访问');
80
+ this.assert(typeof this.sqliteSql.config === 'object', 'SQLite config属性可访问');
81
+ }
82
+
83
+ /**
84
+ * 检查SQL构建兼容性
85
+ */
86
+ checkSqlBuildCompat() {
87
+ console.log('\n=== 测试SQL构建方法兼容性 ===');
88
+
89
+ const test_param = {
90
+ name: '测试用户',
91
+ age: 25
92
+ };
93
+ const test_tpl = {
94
+ name: "`name` like '%{0}%'",
95
+ age: '`age` = {0}'
96
+ };
97
+
98
+ // 测试tplQuery方法
99
+ const mysql_query = this.mysqlSql.tplQuery(test_param, test_tpl);
100
+ const sqlite_query = this.sqliteSql.tplQuery(test_param, test_tpl);
101
+ this.assert(typeof mysql_query === 'string', 'MySQL tplQuery方法返回字符串');
102
+ this.assert(typeof sqlite_query === 'string', 'SQLite tplQuery方法返回字符串');
103
+ this.assert(mysql_query === sqlite_query, 'MySQL和SQLite tplQuery结果一致');
104
+
105
+ // 测试tplBody方法
106
+ const mysql_body = this.mysqlSql.tplBody(test_param, test_tpl);
107
+ const sqlite_body = this.sqliteSql.tplBody(test_param, test_tpl);
108
+ this.assert(typeof mysql_body === 'string', 'MySQL tplBody方法返回字符串');
109
+ this.assert(typeof sqlite_body === 'string', 'SQLite tplBody方法返回字符串');
110
+ this.assert(mysql_body === sqlite_body, 'MySQL和SQLite tplBody结果一致');
111
+ }
112
+
113
+ /**
114
+ * 检查数据库操作兼容性
115
+ */
116
+ async checkDbOpCompat() {
117
+ console.log('\n=== 测试数据库操作兼容性 ===');
118
+
119
+ const test_table = 'test_compatibility';
120
+ const create_sql = `CREATE TABLE IF NOT EXISTS ${test_table} (
121
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
122
+ name VARCHAR(50) NOT NULL,
123
+ value INT DEFAULT 0
124
+ )`;
125
+
126
+ // 测试exec方法
127
+ try {
128
+ const mysql_exec = await this.mysqlSql.exec(create_sql);
129
+ const sqlite_exec = await this.sqliteSql.exec(create_sql);
130
+ this.assert(typeof mysql_exec === 'number', 'MySQL exec方法返回数字');
131
+ this.assert(typeof sqlite_exec === 'number', 'SQLite exec方法返回数字');
132
+ } catch {
133
+ console.log('⚠️ exec方法测试跳过(可能没有真实数据库连接)');
134
+ }
135
+
136
+ // 测试run方法(使用模拟数据)
137
+ try {
138
+ const select_sql = 'SELECT 1 as test_value';
139
+ const mysql_run = await this.mysqlSql.run(select_sql);
140
+ const sqlite_run = await this.sqliteSql.run(select_sql);
141
+
142
+ // 检查返回类型
143
+ this.assert(Array.isArray(mysql_run) || mysql_run === null, 'MySQL run方法返回数组或null');
144
+ this.assert(Array.isArray(sqlite_run) || sqlite_run === null, 'SQLite run方法返回数组或null');
145
+ } catch {
146
+ console.log('⚠️ run方法测试跳过(可能没有真实数据库连接)');
147
+ }
148
+
149
+ // 清理测试表
150
+ try {
151
+ await this.mysqlSql.exec(`DROP TABLE IF EXISTS ${test_table}`);
152
+ await this.sqliteSql.exec(`DROP TABLE IF EXISTS ${test_table}`);
153
+ } catch {
154
+ // 忽略清理错误
155
+ }
156
+ }
157
+
158
+ /**
159
+ * 检查错误处理兼容性
160
+ */
161
+ async checkErrorCompat() {
162
+ console.log('\n=== 测试错误处理兼容性 ===');
163
+
164
+ // 测试无效SQL(根据要求,run方法应该返回空数组而不是抛出错误)
165
+ try {
166
+ // 使用有效的SQL语法但无效的表名,确保通过参数验证
167
+ const mysql_rows = await this.mysqlSql.run('SELECT * FROM non_existent_table_xyz', []);
168
+ // 根据要求,run方法在错误时应该返回空数组[]
169
+ this.assert(Array.isArray(mysql_rows), 'MySQL run方法对无效SQL返回数组');
170
+ this.assert(mysql_rows.length === 0, 'MySQL run方法对无效SQL返回空数组');
171
+
172
+ // 检查错误信息是否写入this.error
173
+ this.assert(this.mysqlSql.error !== null, 'MySQL错误信息写入this.error');
174
+ console.log('MySQL错误信息:', this.mysqlSql.error);
175
+ } catch (error) {
176
+ console.log('❌ MySQL run方法抛出了错误而不是返回空数组:', error.message);
177
+ this.assert(false, 'MySQL run方法应该返回空数组而不是抛出错误');
178
+ }
179
+
180
+ try {
181
+ // 使用有效的SQL语法但无效的表名,确保通过参数验证
182
+ const sqlite_rows = await this.sqliteSql.run('SELECT * FROM non_existent_table_xyz', [], 30000);
183
+ // 根据要求,run方法在错误时应该返回空数组[]
184
+ this.assert(Array.isArray(sqlite_rows), 'SQLite run方法对无效SQL返回数组');
185
+ this.assert(sqlite_rows.length === 0, 'SQLite run方法对无效SQL返回空数组');
186
+
187
+ // 检查错误信息是否写入this.error
188
+ this.assert(this.sqliteSql.error !== null, 'SQLite错误信息写入this.error');
189
+ console.log('SQLite错误信息:', this.sqliteSql.error);
190
+ } catch (error) {
191
+ console.log('❌ SQLite run方法抛出了错误而不是返回空数组:', error.message);
192
+ this.assert(false, 'SQLite run方法应该返回空数组而不是抛出错误');
193
+ }
194
+
195
+ // 测试参数验证
196
+ try {
197
+ await this.mysqlSql.run(123); // 无效参数类型
198
+ this.assert(false, 'MySQL应该对无效参数抛出TypeError');
199
+ } catch (error) {
200
+ this.assert(error instanceof TypeError, 'MySQL对无效参数正确抛出TypeError');
201
+ }
202
+
203
+ try {
204
+ await this.sqliteSql.run(123); // 无效参数类型
205
+ this.assert(false, 'SQLite应该对无效参数抛出TypeError');
206
+ } catch (error) {
207
+ this.assert(error instanceof TypeError, 'SQLite对无效参数正确抛出TypeError');
208
+ }
209
+ }
210
+
211
+ /**
212
+ * 适配器类型
213
+ * @returns {void}
214
+ */
215
+ type() {
216
+ const mysql_adapter_type = this.mysqlSql._adapter?.constructor?.name || 'Unknown';
217
+ const sqlite_adapter_type = this.sqliteSql._adapter?.constructor?.name || 'Unknown';
218
+
219
+ this.assert(mysql_adapter_type === 'Mysql',
220
+ `MySQL适配器类型正确: ${mysql_adapter_type}`);
221
+ this.assert(sqlite_adapter_type === 'SQLite',
222
+ `SQLite适配器类型正确: ${sqlite_adapter_type}`);
223
+ }
224
+
225
+ /**
226
+ * 适配器方法
227
+ * @returns {void}
228
+ */
229
+ method() {
230
+ const mysql_has_run = typeof this.mysqlSql._adapter?.run === 'function';
231
+ const sqlite_has_run = typeof this.sqliteSql._adapter?.run === 'function';
232
+ const mysql_has_exec = typeof this.mysqlSql._adapter?.exec === 'function';
233
+ const sqlite_has_exec = typeof this.sqliteSql._adapter?.exec === 'function';
234
+
235
+ this.assert(mysql_has_run, 'MySQL适配器有run方法');
236
+ this.assert(sqlite_has_run, 'SQLite适配器有run方法');
237
+ this.assert(mysql_has_exec, 'MySQL适配器有exec方法');
238
+ this.assert(sqlite_has_exec, 'SQLite适配器有exec方法');
239
+ }
240
+
241
+ /**
242
+ * 适配器
243
+ * @returns {void}
244
+ */
245
+ adapter() {
246
+ console.log('\n=== 检查适配器切换 ===');
247
+ this.type();
248
+ this.method();
249
+ }
250
+
251
+ /**
252
+ * 运行所有兼容性测试
253
+ * @returns {Promise<void>}
254
+ */
255
+ async runAllTests() {
256
+ console.log('开始MySQL和SQLite兼容性测试...\n');
257
+
258
+ if (!await this.init()) {
259
+ console.log('❌ 初始化失败,跳过所有测试');
260
+ return;
261
+ }
262
+
263
+ try {
264
+ this.checkConfigCompat();
265
+ this.checkSqlBuildCompat();
266
+ await this.checkDbOpCompat();
267
+ await this.checkErrorCompat();
268
+ this.adapter();
269
+
270
+ console.log('\n=== 兼容性测试结果 ===');
271
+ console.log(`总计: ${this.total} 个测试`);
272
+ console.log(`通过: ${this.passed} 个`);
273
+ console.log(`失败: ${this.failed} 个`);
274
+ console.log(`成功率: ${((this.passed / this.total) * 100).toFixed(2)}%`);
275
+
276
+ if (this.failed === 0) {
277
+ console.log('\n🎉 MySQL和SQLite完全兼容!');
278
+ } else {
279
+ console.log('\n⚠️ 存在兼容性问题,需要进一步检查');
280
+ }
281
+
282
+ return {
283
+ success: this.failed === 0,
284
+ passed: this.passed,
285
+ failed: this.failed,
286
+ total: this.total
287
+ };
288
+ } catch (error) {
289
+ console.error('测试执行过程中发生错误:', error);
290
+ return {
291
+ success: false,
292
+ passed: this.passed,
293
+ failed: this.failed,
294
+ total: this.total,
295
+ error: error.message
296
+ };
297
+ }
298
+ }
299
+ }
300
+
301
+ // 导出测试类
302
+ module.exports = SqlCompatTest;
303
+
304
+ // 如果直接运行此文件,则执行测试
305
+ if (require.main === module) {
306
+ const test = new SqlCompatTest();
307
+ test.runAllTests().then(result => {
308
+ console.log('测试完成:', result);
309
+ }).catch(error => {
310
+ console.error('测试失败:', error);
311
+ });
312
+ }
313
+
314
+ // 如果直接运行此文件,则执行测试
315
+ if (require.main === module) {
316
+ (async function() {
317
+ const test = new SqlCompatibilityTest();
318
+ await test.runAllTests();
319
+ })();
320
+ }