diff --git a/example.js b/example.js new file mode 100644 index 0000000..46bed50 --- /dev/null +++ b/example.js @@ -0,0 +1,41 @@ +MySQL.ready(async function () { + // Async/Await demo: + + console.log(await MySQL.Async.fetchScalar("SELECT @parameters", { "@parameters": 1 })) + + console.log(await MySQL.Async.fetchScalar("SELECT @parameters", { "@parameters": "Hello World" })) + + console.log(await MySQL.Async.fetchAll("SELECT * FROM whitelist", {})) + + console.log(await MySQL.Async.insert("INSERT INTO players(name, money, location) VALUES (@name, @money, @location)", + { + "@name": "Player 1", + "@money": 5000, + "@location": JSON.stringify({ x: 50, y: 10, z: 20 }) + }) + ) + + // Callback demo: + MySQL.fetchScalar("SELECT @parameters", { "@parameters": 1 }, function (result) { + console.log(result); + }) + + MySQL.fetchScalar("SELECT @parameters", { "@parameters": "Hello Callback!" }, function (result) { + console.log(result); + }) + + MySQL.fetchAll("SELECT * FROM whitelist", {}, function (results) { + console.log(results); + }) + + MySQL.insert("INSERT INTO players(name, money, location) VALUES (@name, @money, @location)", + { + "@name": "Player 1", + "@money": 5000, + "@location": JSON.stringify({ x: 50, y: 10, z: 20 }) + }, + function (insertId) { + console.log(insertId) + } + ) +}) \ No newline at end of file diff --git a/lib/MySQL.js b/lib/MySQL.js new file mode 100644 index 0000000..8685ac5 --- /dev/null +++ b/lib/MySQL.js @@ -0,0 +1,164 @@ +var MySQL = { + Async: {}, +} + +function safeParameters(params) { + if (!params) return { "": "" }; + if (typeof (params) != "object") throw new Error("A Object is expected"); + if (!Object.keys(params).length) return { "": "" }; + + return params +} + +/** + * Execute a query with no result required, sync version + * @param {String} query + * @param {Object} params + * @returns Promise (Number of rows updated) + */ +MySQL.Async.execute = (query, params) => { + if (typeof (query) != "string" && typeof (query) != "number") throw new Error("The SQL Query must be a string") + return new Promise((resolve, reject) => { + exports['mysql-async'].mysql_execute(query, safeParameters(params), function (result) { + resolve(result); + }) + }) +} + +/** + * Execute a query and fetch all results, sync version + * @param {String} query + * @param {Object} params + * @returns Promise (Array of Query results) + */ +MySQL.Async.fetchAll = (query, params) => { + if (typeof (query) != "string" && typeof (query) != "number") throw new Error("The SQL Query must be a string") + return new Promise((resolve, reject) => { + exports['mysql-async'].mysql_fetch_all(query, safeParameters(params), function (result) { + resolve(result); + }) + }) +} + +/** + * Execute a query and fetch the first column of the first row, sync version + * Useful for count function by example + * @param {String} query + * @param {Object} params + * @returns Promise (mixed Value of the first column in the first row) + */ +MySQL.Async.fetchScalar = (query, params) => { + if (typeof (query) != "string" && typeof (query) != "number") throw new Error("The SQL Query must be a string") + return new Promise((resolve, reject) => { + exports['mysql-async'].mysql_fetch_scalar(query, safeParameters(params), function (result) { + resolve(result); + }) + }) +} + +/** + * Execute a query and retrieve the last id insert, sync version + * @param {String} query + * @param {Object} params + * @returns Promise (mixed Value of the last insert id) + */ +MySQL.Async.insert = (query, params) => { + if (typeof (query) != "string" && typeof (query) != "number") throw new Error("The SQL Query must be a string") + return new Promise((resolve, reject) => { + exports['mysql-async'].mysql_insert(query, safeParameters(params), function (result) { + resolve(result); + }) + }) +} + +/** + * Stores a query for later execution + * @param {String} query + * @returns Promise + */ +MySQL.Async.store = (query) => { + if (typeof (query) != "string") throw new Error("The SQL Query must be a string") + return new Promise((resolve, reject) => { + exports['mysql-async'].mysql_store(query, function (result) { + resolve(result); + }) + }) +} + +/** + * Execute a List of querys and returns bool true when all are executed successfully + * @param {Array} querys + * @param {Object} params + * @returns Promise (bool if the transaction was successful) + */ +MySQL.Async.transaction = (querys, params) => { + return new Promise((resolve, reject) => { + exports['mysql-async'].mysql_transaction(querys, params, function (result) { + resolve(result); + }) + }) +} + +/** + * Execute a query with no result required, async version + * @param {String} query + * @param {Object} params + * @param {Function} func + */ +MySQL.fetchAll = (query, params, func) => { + if (typeof (query) != "string" && typeof (query) != "number") throw new Error("The SQL Query must be a string") + exports['mysql-async'].mysql_fetch_all(query, safeParameters(params), func) +} + +/** + * Execute a query and fetch the first column of the first row, async version + * Useful for count function by example + * @param {String} query + * @param {Object} params + * @param {Function} func + */ +MySQL.fetchScalar = (query, params, func) => { + if (typeof (query) != "string" && typeof (query) != "number") throw new Error("The SQL Query must be a string") + exports['mysql-async'].mysql_fetch_scalar(query, safeParameters(params), func) +} + +/** + * Execute a query and retrieve the last id insert, async version + * @param {String} query + * @param {Object} params + * @param {Function} func + */ +MySQL.insert = (query, params, func) => { + if (typeof (query) != "string" && typeof (query) != "number") throw new Error("The SQL Query must be a string") + exports['mysql-async'].mysql_insert(query, safeParameters(params), func) +} + +/** + * Stores a query for later execution + * @param {String} query + * @param {Function} func + */ +MySQL.store = (query, func) => { + if (typeof (query) != "string") throw new Error("The SQL Query must be a string") + exports['mysql-async'].mysql_store(query, func) +} + +/** + * Execute a List of querys and returns bool true when all are executed successfully + * @param {Array} querys + * @param {Object} params + * @param {Function} func + * @returns Bool true if successfully + */ +MySQL.transaction = (querys, params, func) => { + return exports['mysql-async'].mysql_transaction(querys, params, func) +} + +MySQL.ready = (callback) => { + const i = setInterval(() => { + if (GetResourceState('mysql-async') != 'started') return; + if (!exports['mysql-async'].is_ready()) return; + clearInterval(i); + callback() + }, 100) +} \ No newline at end of file diff --git a/src/docs/content/pages/queries.md b/src/docs/content/pages/queries.md index 43b72ef..029f8da 100644 --- a/src/docs/content/pages/queries.md +++ b/src/docs/content/pages/queries.md @@ -10,6 +10,7 @@ Contrary to older *Sync* implementations, these functions are safe to use, since ### MySQL.ready You need to encapsulate your code into `MySQL.ready` to be sure that the mod will be available and initialized before your first request. In subsequent examples the `MySQL.ready` function will not be shown, and it is expected that the code is encapsulated. +#### Lua ```lua MySQL.ready(function () print(MySQL.Sync.fetchScalar('SELECT @parameters', { @@ -23,10 +24,26 @@ string ]]-- ``` +#### JavaScript +```javascript +MySQL.ready(function () { + console.log(await MySQL.Async.fetchScalar('SELECT @parameters', { + '@parameters': 'string' + } + )) +}) +/* +prints: + +string +*/ +``` + ### execute Execute a mysql query which should not send any result (like a Insert / Delete / Update), and will return the number of affected rows. +#### Lua ```lua MySQL.Async.execute('INSERT INTO users_log (x, y, z, playerId) VALUES (@x, @y, @z, @id)', { ['x'] = pos.x, ['y'] = pos.y, ['z'] = pos.z, ['id'] = player.id }, @@ -41,10 +58,25 @@ prints: ]]-- ``` +#### JavaScript +```javascript +console.log(await MySQL.Async.execute('INSERT INTO users_log (x, y, z, playerId) VALUES (@x, @y, @z, @id)', + { '@x': pos.x, '@y': pos.y, '@z': pos.z, '@id': player.id } +)) +/* +prints: + +1 +*/ +``` + + + ### fetchAll Fetch results from MySQL and returns them in the form of an Array of Objects: +#### Lua ```lua MySQL.Async.fetchAll('SELECT * FROM users WHERE id = @id', { ['@id'] = playerId }, function(result) print(json.encode(result)) @@ -63,9 +95,30 @@ prints: ]]-- ``` +#### JavaScript +```javascript +console.log(await MySQL.Async.fetchAll('SELECT * FROM users WHERE id = @id', + { '@id': playerId } +)) +/* +prints: + +[{ + "id": 95585726093402110, + "cash": 0, + "bank": 0, + "skin": "{}", + "online": true, + "lastSeen": 1590656804000 +}] +*/ +``` + ### fetchScalar Fetch the first field of the first row in a query: + +#### Lua ```lua MySQL.Async.fetchScalar('SELECT COUNT(1) FROM users', {}, function(result) print(result) @@ -77,10 +130,21 @@ prints: ]]-- ``` +#### JavaScript +```javascript +console.log(await MySQL.Async.fetchScalar('SELECT COUNT(1) FROM users', {} )) +/* +prints: + +15 +*/ +``` + ### insert Returns the last insert id of the inserted item. Needs an auto-incremented primary key to work. +#### Lua ```lua MySQL.Async.insert('INSERT INTO users_log (x, y, z, playerId) VALUES (@x, @y, @z, @id)', { ['x'] = pos.x, ['y'] = pos.y, ['z'] = pos.z, ['id'] = player.id }, @@ -95,12 +159,25 @@ prints: ]]-- ``` +#### JavaScript +```javascript +console.log(await MySQL.Async.fetchScalar('INSERT INTO users_log (x, y, z, playerId) VALUES (@x, @y, @z, @id)', + { '@x': pos.x, '@y': pos.y, '@z': pos.z, '@id': player.id } +)) +/* +prints: + +1137 +*/ +``` + ### store The store export should be used for storing query strings, when a lot of queries are expected to be triggered at once. The idea behind this feature is, that while recieving data puts stress on your server infrastructure, so does sending data. And the biggest polluter for this resource is sending overly long and complicated query strings. While the server is running you want to minimize the impact of sending a lot of queries at once puts on your architecture, thus you can already store these queries ahead of time, and just pass the id returned by the callback function and pass the parameters for these queries along. +#### Lua ```lua insertUserLog = -1 MySQL.Async.store("INSERT INTO users_log SET ?", function(storeId) insertUserLog = storeId end) @@ -112,4 +189,38 @@ MySQL.Async.insert(insertUserLog, { end) ``` +#### JavaScript +```javascript +var insertUserLog = await MySQL.Async.store('INSERT INTO users_log SET ?'); +// ... +console.log(await MySQL.Async.insert(insertUserLog, + { '@x': pos.x, '@y': pos.y, '@z': pos.z, '@id': player.id } +)) +``` + This works like the example above, but the query string does not need to be reset and is a bit more elegant in the writing. + + +Every JavaScript function is also available as a callback function. +Just leave out the ".Async" keyword and add a callback as the last argument. +#### JavaScript +```javascript +MySQL.fetchAll('SELECT * FROM users WHERE id = @id', + { '@id': playerId }, + function (results) { + console.log(results); + } +) +/* +prints: + +[{ + "id": 95585726093402110, + "cash": 0, + "bank": 0, + "skin": "{}", + "online": true, + "lastSeen": 1590656804000 +}] +*/ +``` \ No newline at end of file