@onurege3467/zerohelper 7.0.0 → 7.2.0
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/database/cacheWrapper.js +168 -55
- package/database/index.js +5 -2
- package/database/mysql.js +67 -54
- package/database/pg.js +67 -77
- package/database/redis.js +162 -0
- package/database/sqlite.js +159 -31
- package/package.json +4 -6
- package/readme.md +35 -0
- package/data/test_db.json +0 -3
- package/data/test_db.sqlite +0 -0
- package/data/test_db_cached.sqlite +0 -0
- package/test copy.js +0 -144
- package/test.js +0 -261
package/database/cacheWrapper.js
CHANGED
|
@@ -1,120 +1,233 @@
|
|
|
1
|
+
// cacheWrapper.js - Redis@4 ve Memory Cache Uyumlu
|
|
1
2
|
const { LRUCache } = require('lru-cache');
|
|
3
|
+
const { createClient } = require('redis');
|
|
2
4
|
|
|
3
5
|
class CacheWrapper {
|
|
4
6
|
constructor(databaseInstance, options = {}) {
|
|
5
7
|
this.db = databaseInstance;
|
|
8
|
+
this.cacheType = options.type || 'memory'; // 'memory' or 'redis'
|
|
9
|
+
this.tableCaches = {};
|
|
10
|
+
|
|
11
|
+
if (this.cacheType === 'redis') {
|
|
12
|
+
this._initRedisCache(options);
|
|
13
|
+
} else {
|
|
14
|
+
this._initMemoryCache(options);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
_initMemoryCache(options) {
|
|
6
19
|
this.cache = new LRUCache({
|
|
7
|
-
max: options.max || 500,
|
|
8
|
-
ttl: options.ttl || 1000 * 60 * 5,
|
|
9
|
-
updateAgeOnGet: options.updateAgeOnGet
|
|
20
|
+
max: options.max || 500,
|
|
21
|
+
ttl: options.ttl || 1000 * 60 * 5,
|
|
22
|
+
updateAgeOnGet: options.updateAgeOnGet || false,
|
|
23
|
+
});
|
|
24
|
+
this.redisAvailable = false;
|
|
25
|
+
this.redisClient = null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async _initRedisCache(options) {
|
|
29
|
+
const redisConfig = {
|
|
30
|
+
socket: {
|
|
31
|
+
host: options.host || '127.0.0.1',
|
|
32
|
+
port: options.port || 6379,
|
|
33
|
+
connectTimeout: options.connectTimeout || 5000,
|
|
34
|
+
},
|
|
35
|
+
password: options.password,
|
|
36
|
+
database: options.db || 0,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
this.redisClient = createClient(redisConfig);
|
|
40
|
+
this.ttl = options.ttl || 300; // 5 dakika (saniye cinsinden)
|
|
41
|
+
this.keyPrefix = options.keyPrefix || 'db_cache:';
|
|
42
|
+
this.redisAvailable = false;
|
|
43
|
+
|
|
44
|
+
this.redisClient.on('error', (err) => {
|
|
45
|
+
console.warn('Redis Cache Error, memory cache fallback:', err.message);
|
|
46
|
+
this.redisAvailable = false;
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
this.redisClient.on('connect', () => {
|
|
50
|
+
console.log('Redis Cache Connected');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
this.redisClient.on('ready', () => {
|
|
54
|
+
console.log('Redis Cache Ready');
|
|
55
|
+
this.redisAvailable = true;
|
|
10
56
|
});
|
|
11
|
-
|
|
57
|
+
|
|
58
|
+
this.redisClient.on('end', () => {
|
|
59
|
+
console.log('Redis Cache Disconnected');
|
|
60
|
+
this.redisAvailable = false;
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
await Promise.race([
|
|
65
|
+
this.redisClient.connect(),
|
|
66
|
+
new Promise((_, reject) =>
|
|
67
|
+
setTimeout(() => reject(new Error('Redis connection timeout')), 5000)
|
|
68
|
+
),
|
|
69
|
+
]);
|
|
70
|
+
this.redisAvailable = true;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.warn('Redis bağlantısı başarısız, memory cache kullanılacak:', error.message);
|
|
73
|
+
this._initMemoryCache(options);
|
|
74
|
+
this.redisClient.removeAllListeners();
|
|
75
|
+
this.redisClient = null;
|
|
76
|
+
this.redisAvailable = false;
|
|
77
|
+
}
|
|
12
78
|
}
|
|
13
79
|
|
|
14
80
|
_getCache(table) {
|
|
81
|
+
if (this.cacheType === 'redis' && this.redisAvailable && this.redisClient) {
|
|
82
|
+
return this.redisClient;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Memory fallback
|
|
15
86
|
if (!this.tableCaches[table]) {
|
|
16
87
|
this.tableCaches[table] = new LRUCache({
|
|
17
|
-
max: this.cache.max,
|
|
18
|
-
ttl: this.cache.ttl,
|
|
19
|
-
updateAgeOnGet: this.cache.updateAgeOnGet,
|
|
88
|
+
max: this.cache ? this.cache.max : 500,
|
|
89
|
+
ttl: this.cache ? this.cache.ttl : 300000,
|
|
90
|
+
updateAgeOnGet: this.cache ? this.cache.updateAgeOnGet : false,
|
|
20
91
|
});
|
|
21
92
|
}
|
|
22
93
|
return this.tableCaches[table];
|
|
23
94
|
}
|
|
24
95
|
|
|
25
96
|
_generateKey(table, where) {
|
|
26
|
-
|
|
97
|
+
const key = `${table}:${JSON.stringify(where || {})}`;
|
|
98
|
+
return this.cacheType === 'redis' ? `${this.keyPrefix}${key}` : key;
|
|
27
99
|
}
|
|
28
100
|
|
|
29
|
-
|
|
101
|
+
async _getCacheValue(cache, key) {
|
|
102
|
+
if (this.cacheType === 'redis' && this.redisAvailable && this.redisClient) {
|
|
103
|
+
try {
|
|
104
|
+
const value = await cache.get(key);
|
|
105
|
+
return value ? JSON.parse(value) : null;
|
|
106
|
+
} catch (err) {
|
|
107
|
+
console.warn('Redis get error, memory fallback:', err.message);
|
|
108
|
+
this.redisAvailable = false;
|
|
109
|
+
return this._getCache(key.split(':')[1]).get(key);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
return cache.get(key);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async _setCacheValue(cache, key, value) {
|
|
117
|
+
if (this.cacheType === 'redis' && this.redisAvailable && this.redisClient) {
|
|
118
|
+
try {
|
|
119
|
+
await cache.setEx(key, this.ttl, JSON.stringify(value));
|
|
120
|
+
} catch (err) {
|
|
121
|
+
console.warn('Redis set error, memory fallback:', err.message);
|
|
122
|
+
this.redisAvailable = false;
|
|
123
|
+
this._getCache(key.split(':')[1]).set(key, value);
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
cache.set(key, value);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async _clearCache(table) {
|
|
131
|
+
const cache = this._getCache(table);
|
|
132
|
+
if (this.cacheType === 'redis' && this.redisAvailable && this.redisClient) {
|
|
133
|
+
try {
|
|
134
|
+
const keys = await this.redisClient.keys(`${this.keyPrefix}${table}:*`);
|
|
135
|
+
if (keys.length) await this.redisClient.del(keys);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
console.warn('Redis clear error, memory fallback:', err.message);
|
|
138
|
+
this.redisAvailable = false;
|
|
139
|
+
cache.clear();
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
cache.clear();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Read
|
|
30
147
|
async select(table, where) {
|
|
31
148
|
const cache = this._getCache(table);
|
|
32
149
|
const key = this._generateKey(table, where);
|
|
33
|
-
let data =
|
|
34
|
-
|
|
35
|
-
if (data) {
|
|
36
|
-
// console.log(`Cache hit for select: ${key}`);
|
|
37
|
-
return data;
|
|
38
|
-
}
|
|
150
|
+
let data = await this._getCacheValue(cache, key);
|
|
151
|
+
if (data) return data;
|
|
39
152
|
|
|
40
|
-
// console.log(`Cache miss for select: ${key}`);
|
|
41
153
|
data = await this.db.select(table, where);
|
|
42
|
-
|
|
154
|
+
await this._setCacheValue(cache, key, data);
|
|
43
155
|
return data;
|
|
44
156
|
}
|
|
45
157
|
|
|
46
158
|
async selectOne(table, where) {
|
|
47
159
|
const cache = this._getCache(table);
|
|
48
160
|
const key = this._generateKey(table, where);
|
|
49
|
-
let data =
|
|
161
|
+
let data = await this._getCacheValue(cache, key);
|
|
162
|
+
if (data) return data;
|
|
50
163
|
|
|
51
|
-
if (data) {
|
|
52
|
-
// console.log(`Cache hit for selectOne: ${key}`);
|
|
53
|
-
return data;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// console.log(`Cache miss for selectOne: ${key}`);
|
|
57
164
|
data = await this.db.selectOne(table, where);
|
|
58
|
-
|
|
165
|
+
await this._setCacheValue(cache, key, data);
|
|
59
166
|
return data;
|
|
60
167
|
}
|
|
61
168
|
|
|
62
|
-
// Write
|
|
169
|
+
// Write (invalidate)
|
|
63
170
|
async insert(table, data) {
|
|
64
|
-
this.
|
|
171
|
+
await this._clearCache(table);
|
|
65
172
|
return this.db.insert(table, data);
|
|
66
173
|
}
|
|
67
174
|
|
|
68
175
|
async update(table, data, where) {
|
|
69
|
-
this.
|
|
176
|
+
await this._clearCache(table);
|
|
70
177
|
return this.db.update(table, data, where);
|
|
71
178
|
}
|
|
72
179
|
|
|
73
180
|
async set(table, data, where) {
|
|
74
|
-
this.
|
|
181
|
+
await this._clearCache(table);
|
|
75
182
|
return this.db.set(table, data, where);
|
|
76
183
|
}
|
|
77
184
|
|
|
78
185
|
async delete(table, where) {
|
|
79
|
-
this.
|
|
186
|
+
await this._clearCache(table);
|
|
80
187
|
return this.db.delete(table, where);
|
|
81
188
|
}
|
|
82
189
|
|
|
83
|
-
async bulkInsert(table, dataArray) {
|
|
84
|
-
this._getCache(table).clear();
|
|
85
|
-
return this.db.bulkInsert(table, dataArray);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
190
|
async deleteOne(table, where) {
|
|
89
|
-
this.
|
|
191
|
+
await this._clearCache(table);
|
|
90
192
|
return this.db.deleteOne(table, where);
|
|
91
193
|
}
|
|
92
194
|
|
|
93
195
|
async updateOne(table, data, where) {
|
|
94
|
-
this.
|
|
196
|
+
await this._clearCache(table);
|
|
95
197
|
return this.db.updateOne(table, data, where);
|
|
96
198
|
}
|
|
97
199
|
|
|
98
|
-
|
|
200
|
+
async bulkInsert(table, dataArray) {
|
|
201
|
+
await this._clearCache(table);
|
|
202
|
+
return this.db.bulkInsert(table, dataArray);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async clearAllCache() {
|
|
206
|
+
if (this.cacheType === 'redis' && this.redisAvailable && this.redisClient) {
|
|
207
|
+
try {
|
|
208
|
+
const keys = await this.redisClient.keys(`${this.keyPrefix}*`);
|
|
209
|
+
if (keys.length) await this.redisClient.del(keys);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
console.warn('Redis clearAll error, memory fallback:', err.message);
|
|
212
|
+
this.redisAvailable = false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
Object.values(this.tableCaches).forEach(c => c.clear());
|
|
216
|
+
if (this.cache) this.cache.clear();
|
|
217
|
+
}
|
|
218
|
+
|
|
99
219
|
async close() {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
// if the underlying db instance has the method before calling.
|
|
110
|
-
get(target, prop) {
|
|
111
|
-
if (typeof this[prop] !== 'undefined') {
|
|
112
|
-
return this[prop];
|
|
113
|
-
} else if (typeof this.db[prop] === 'function') {
|
|
114
|
-
return this.db[prop].bind(this.db);
|
|
115
|
-
} else {
|
|
116
|
-
return this.db[prop];
|
|
220
|
+
await this.clearAllCache();
|
|
221
|
+
if (this.cacheType === 'redis' && this.redisClient) {
|
|
222
|
+
try {
|
|
223
|
+
this.redisClient.removeAllListeners();
|
|
224
|
+
if (this.redisAvailable) await this.redisClient.quit();
|
|
225
|
+
else await this.redisClient.disconnect();
|
|
226
|
+
} catch {}
|
|
227
|
+
this.redisClient = null;
|
|
228
|
+
this.redisAvailable = false;
|
|
117
229
|
}
|
|
230
|
+
return this.db.close ? this.db.close() : null;
|
|
118
231
|
}
|
|
119
232
|
}
|
|
120
233
|
|
package/database/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
// database-factory.js
|
|
1
2
|
const IDatabase = require('./IDatabase');
|
|
2
3
|
const MySQLDatabase = require('./mysql');
|
|
3
4
|
const SQLiteDatabase = require('./sqlite');
|
|
4
5
|
const MongoDBDatabase = require('./mongodb');
|
|
5
6
|
const JsonDatabase = require('./json');
|
|
6
7
|
const PostgreSQLDatabase = require('./pg');
|
|
8
|
+
const RedisDatabase = require('./redis'); // Yeni Redis adapter
|
|
7
9
|
const CacheWrapper = require('./cacheWrapper');
|
|
8
10
|
|
|
9
11
|
const adapters = {
|
|
@@ -12,6 +14,7 @@ const adapters = {
|
|
|
12
14
|
mongodb: MongoDBDatabase,
|
|
13
15
|
postgres: PostgreSQLDatabase,
|
|
14
16
|
json: JsonDatabase,
|
|
17
|
+
redis: RedisDatabase, // Redis adapter eklendi
|
|
15
18
|
};
|
|
16
19
|
|
|
17
20
|
/**
|
|
@@ -19,7 +22,7 @@ const adapters = {
|
|
|
19
22
|
* Bu bir "Fabrika Fonksiyonu"dur.
|
|
20
23
|
*
|
|
21
24
|
* @param {object} options - Yapılandırma seçenekleri.
|
|
22
|
-
* @param {keyof adapters} options.adapter - Kullanılacak adaptör ('mysql', 'sqlite', vb.).
|
|
25
|
+
* @param {keyof adapters} options.adapter - Kullanılacak adaptör ('mysql', 'sqlite', 'redis', vb.).
|
|
23
26
|
* @param {object} options.config - Seçilen adaptöre özel yapılandırma.
|
|
24
27
|
* @returns {IDatabase} - IDatabase arayüzünü uygulayan bir örnek döndürür.
|
|
25
28
|
*/
|
|
@@ -62,4 +65,4 @@ function createDatabase(options) {
|
|
|
62
65
|
module.exports = createDatabase;
|
|
63
66
|
|
|
64
67
|
// Eğer hem fonksiyonu hem de tipleri export etmek isterseniz:
|
|
65
|
-
// module.exports = { createDatabase };
|
|
68
|
+
// module.exports = { createDatabase };
|
package/database/mysql.js
CHANGED
|
@@ -169,6 +169,25 @@ class MySQLDatabase extends IDatabase{
|
|
|
169
169
|
return bestType;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Eksik kolonları kontrol eder ve ekler
|
|
174
|
+
*/
|
|
175
|
+
async _ensureMissingColumns(table, data) {
|
|
176
|
+
const existingColumns = await this.query(`DESCRIBE \`${table}\``).catch(() => null);
|
|
177
|
+
if (!existingColumns) throw new Error(`Table ${table} does not exist.`);
|
|
178
|
+
|
|
179
|
+
const existingColumnNames = existingColumns.map(col => col.Field);
|
|
180
|
+
|
|
181
|
+
for (const key of Object.keys(data)) {
|
|
182
|
+
if (!existingColumnNames.includes(key)) {
|
|
183
|
+
const columnType = this._getColumnType(data[key]);
|
|
184
|
+
const alterSQL = `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`;
|
|
185
|
+
await this.query(alterSQL);
|
|
186
|
+
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
172
191
|
async _queueRequest(operation) {
|
|
173
192
|
if (this._connected) {
|
|
174
193
|
return operation();
|
|
@@ -231,10 +250,11 @@ class MySQLDatabase extends IDatabase{
|
|
|
231
250
|
return this._queueRequest(async () => {
|
|
232
251
|
const copy = { ...data };
|
|
233
252
|
await this.ensureTable(table, copy);
|
|
234
|
-
|
|
235
|
-
|
|
253
|
+
|
|
254
|
+
// Eksik kolonları ekle
|
|
255
|
+
await this._ensureMissingColumns(table, copy);
|
|
236
256
|
|
|
237
|
-
const
|
|
257
|
+
const existingColumns = await this.query(`DESCRIBE \`${table}\``);
|
|
238
258
|
const primaryKeyColumn = existingColumns.find(col => col.Key === 'PRI' && col.Extra.includes('auto_increment'));
|
|
239
259
|
|
|
240
260
|
const insertData = { ...copy };
|
|
@@ -243,13 +263,6 @@ class MySQLDatabase extends IDatabase{
|
|
|
243
263
|
delete insertData[primaryKeyColumn.Field];
|
|
244
264
|
}
|
|
245
265
|
|
|
246
|
-
for (const key of Object.keys(insertData)) {
|
|
247
|
-
if (!existingNames.includes(key)) {
|
|
248
|
-
const columnType = this._getColumnType(insertData[key]);
|
|
249
|
-
await this.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
266
|
const keys = Object.keys(insertData);
|
|
254
267
|
const placeholders = keys.map(() => "?").join(",");
|
|
255
268
|
const values = Object.values(insertData).map(value => this._serializeValue(value));
|
|
@@ -263,22 +276,14 @@ class MySQLDatabase extends IDatabase{
|
|
|
263
276
|
async update(table, data, where) {
|
|
264
277
|
return this._queueRequest(async () => {
|
|
265
278
|
await this.ensureTable(table, { ...data, ...where });
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
for (const key of Object.keys(data)) {
|
|
271
|
-
if (!existingColumnNames.includes(key)) {
|
|
272
|
-
const columnType = this._getColumnType(data[key]);
|
|
273
|
-
const alterSQL = `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`;
|
|
274
|
-
await this.query(alterSQL);
|
|
275
|
-
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
279
|
+
|
|
280
|
+
// Eksik kolonları ekle
|
|
281
|
+
await this._ensureMissingColumns(table, { ...data, ...where });
|
|
282
|
+
|
|
278
283
|
const setString = Object.keys(data).map(k => `\`${k}\` = ?`).join(", ");
|
|
279
284
|
const whereString = Object.keys(where).map(k => `\`${k}\` = ?`).join(" AND ");
|
|
280
285
|
const sql = `UPDATE \`${table}\` SET ${setString} WHERE ${whereString}`;
|
|
281
|
-
const result = await this.query(sql, [...Object.values(data), ...Object.values(where)]);
|
|
286
|
+
const result = await this.query(sql, [...Object.values(data).map(v => this._serializeValue(v)), ...Object.values(where).map(v => this._serializeValue(v))]);
|
|
282
287
|
return result.affectedRows;
|
|
283
288
|
});
|
|
284
289
|
}
|
|
@@ -287,9 +292,13 @@ class MySQLDatabase extends IDatabase{
|
|
|
287
292
|
return this._queueRequest(async () => {
|
|
288
293
|
if (!where || Object.keys(where).length === 0) return 0;
|
|
289
294
|
await this.ensureTable(table, { ...where });
|
|
295
|
+
|
|
296
|
+
// Eksik kolonları ekle (where koşulları için)
|
|
297
|
+
await this._ensureMissingColumns(table, where);
|
|
298
|
+
|
|
290
299
|
const whereString = Object.keys(where).map(k => `\`${k}\` = ?`).join(" AND ");
|
|
291
300
|
const sql = `DELETE FROM \`${table}\` WHERE ${whereString}`;
|
|
292
|
-
const result = await this.query(sql, Object.values(where));
|
|
301
|
+
const result = await this.query(sql, Object.values(where).map(v => this._serializeValue(v)));
|
|
293
302
|
return result.affectedRows;
|
|
294
303
|
});
|
|
295
304
|
}
|
|
@@ -297,13 +306,19 @@ class MySQLDatabase extends IDatabase{
|
|
|
297
306
|
async select(table, where = null) {
|
|
298
307
|
return this._queueRequest(async () => {
|
|
299
308
|
await this.ensureTable(table, where || {});
|
|
309
|
+
|
|
310
|
+
// Eğer where koşulu varsa, eksik kolonları ekle
|
|
311
|
+
if (where && Object.keys(where).length > 0) {
|
|
312
|
+
await this._ensureMissingColumns(table, where);
|
|
313
|
+
}
|
|
314
|
+
|
|
300
315
|
let sql = `SELECT * FROM \`${table}\``;
|
|
301
316
|
let params = [];
|
|
302
317
|
|
|
303
318
|
if (where && Object.keys(where).length > 0) {
|
|
304
319
|
const whereString = Object.keys(where).map(k => `\`${k}\` = ?`).join(" AND ");
|
|
305
320
|
sql += ` WHERE ${whereString}`;
|
|
306
|
-
params = Object.values(where);
|
|
321
|
+
params = Object.values(where).map(v => this._serializeValue(v));
|
|
307
322
|
}
|
|
308
323
|
|
|
309
324
|
return (await this.query(sql, params)).map(row => {
|
|
@@ -319,18 +334,10 @@ class MySQLDatabase extends IDatabase{
|
|
|
319
334
|
async set(table, data, where) {
|
|
320
335
|
return this._queueRequest(async () => {
|
|
321
336
|
await this.ensureTable(table, { ...data, ...where });
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
for (const key of Object.keys(data)) {
|
|
327
|
-
if (!existingColumnNames.includes(key)) {
|
|
328
|
-
const columnType = this._getColumnType(data[key]);
|
|
329
|
-
const alterSQL = `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`;
|
|
330
|
-
await this.query(alterSQL);
|
|
331
|
-
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
337
|
+
|
|
338
|
+
// Eksik kolonları ekle
|
|
339
|
+
await this._ensureMissingColumns(table, { ...data, ...where });
|
|
340
|
+
|
|
334
341
|
const existing = await this.select(table, where);
|
|
335
342
|
if (existing.length === 0) {
|
|
336
343
|
return await this.insert(table, { ...where, ...data });
|
|
@@ -356,11 +363,16 @@ class MySQLDatabase extends IDatabase{
|
|
|
356
363
|
|
|
357
364
|
async deleteOne(table, where) {
|
|
358
365
|
return this._queueRequest(async () => {
|
|
366
|
+
await this.ensureTable(table, where);
|
|
367
|
+
|
|
368
|
+
// Eksik kolonları ekle (where koşulları için)
|
|
369
|
+
await this._ensureMissingColumns(table, where);
|
|
370
|
+
|
|
359
371
|
const row = await this.selectOne(table, where);
|
|
360
372
|
if (!row) return 0;
|
|
361
373
|
const whereString = Object.keys(where).map(k => `\`${k}\` = ?`).join(" AND ");
|
|
362
374
|
const sql = `DELETE FROM \`${table}\` WHERE ${whereString} LIMIT 1`;
|
|
363
|
-
const result = await this.query(sql, Object.values(where));
|
|
375
|
+
const result = await this.query(sql, Object.values(where).map(v => this._serializeValue(v)));
|
|
364
376
|
return result.affectedRows;
|
|
365
377
|
});
|
|
366
378
|
}
|
|
@@ -368,22 +380,14 @@ class MySQLDatabase extends IDatabase{
|
|
|
368
380
|
async updateOne(table, data, where) {
|
|
369
381
|
return this._queueRequest(async () => {
|
|
370
382
|
await this.ensureTable(table, { ...data, ...where });
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
for (const key of Object.keys(data)) {
|
|
376
|
-
if (!existingColumnNames.includes(key)) {
|
|
377
|
-
const columnType = this._getColumnType(data[key]);
|
|
378
|
-
const alterSQL = `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`;
|
|
379
|
-
await this.query(alterSQL);
|
|
380
|
-
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
+
|
|
384
|
+
// Eksik kolonları ekle
|
|
385
|
+
await this._ensureMissingColumns(table, { ...data, ...where });
|
|
386
|
+
|
|
383
387
|
const setString = Object.keys(data).map(k => `\`${k}\` = ?`).join(", ");
|
|
384
388
|
const whereString = Object.keys(where).map(k => `\`${k}\` = ?`).join(" AND ");
|
|
385
389
|
const sql = `UPDATE \`${table}\` SET ${setString} WHERE ${whereString} LIMIT 1`;
|
|
386
|
-
const result = await this.query(sql, [...Object.values(data), ...Object.values(where)]);
|
|
390
|
+
const result = await this.query(sql, [...Object.values(data).map(v => this._serializeValue(v)), ...Object.values(where).map(v => this._serializeValue(v))]);
|
|
387
391
|
return result.affectedRows;
|
|
388
392
|
});
|
|
389
393
|
}
|
|
@@ -395,18 +399,27 @@ class MySQLDatabase extends IDatabase{
|
|
|
395
399
|
|
|
396
400
|
const existingColumns = await this.query(`DESCRIBE \`${table}\``);
|
|
397
401
|
const existingColumnNames = existingColumns.map(col => col.Field);
|
|
398
|
-
|
|
402
|
+
|
|
403
|
+
// Tüm datalardan gelen tüm anahtarları topla
|
|
404
|
+
const allKeys = new Set();
|
|
405
|
+
dataArray.forEach(obj => {
|
|
406
|
+
Object.keys(obj).forEach(key => allKeys.add(key));
|
|
407
|
+
});
|
|
399
408
|
|
|
400
409
|
// Eksik kolonları kontrol et ve ekle
|
|
401
|
-
for (const key of
|
|
410
|
+
for (const key of allKeys) {
|
|
402
411
|
if (!existingColumnNames.includes(key)) {
|
|
403
412
|
// Tüm değerleri kontrol ederek en uygun türü belirle
|
|
404
|
-
const columnValues = dataArray
|
|
413
|
+
const columnValues = dataArray
|
|
414
|
+
.map(obj => obj[key])
|
|
415
|
+
.filter(val => val !== undefined && val !== null);
|
|
405
416
|
const columnType = columnValues.length > 0 ? this._getBestColumnType(columnValues) : 'TEXT';
|
|
406
417
|
await this.query(`ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${columnType}`);
|
|
418
|
+
console.log(`Added missing column '${key}' to table '${table}' with type ${columnType}`);
|
|
407
419
|
}
|
|
408
420
|
}
|
|
409
421
|
|
|
422
|
+
const keys = Array.from(allKeys);
|
|
410
423
|
const placeholders = dataArray.map(() => `(${keys.map(() => '?').join(',')})`).join(',');
|
|
411
424
|
const values = dataArray.flatMap(obj => keys.map(k => this._serializeValue(obj[k])));
|
|
412
425
|
const sql = `INSERT INTO \`${table}\` (${keys.map(k => `\`${k}\``).join(",")}) VALUES ${placeholders}`;
|