Bug 1293445 - 1 - Add a get_query_param SQL function. r=mak draft
authorKit Cambridge <kit@yakshaving.ninja>
Tue, 03 Apr 2018 14:23:09 +0200
changeset 778502 deea2bce87803d493fa9e18bb7af0ddcbe89ed7f
parent 778495 5bc815b7de6dc7494e3010f823fb79aa351e8fe3
child 778503 9f0ced3f2345fc3d6ccc46101941e64202d1f06d
push id105499
push usermak77@bonardo.net
push dateFri, 06 Apr 2018 11:20:04 +0000
reviewersmak
bugs1293445
milestone61.0a1
Bug 1293445 - 1 - Add a get_query_param SQL function. r=mak MozReview-Commit-ID: 12r5GyhoYgi
toolkit/components/places/Database.cpp
toolkit/components/places/SQLFunctions.cpp
toolkit/components/places/SQLFunctions.h
toolkit/components/places/tests/unit/test_get_query_param_sql_function.js
toolkit/components/places/tests/unit/xpcshell.ini
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -1440,16 +1440,18 @@ Database::InitFunctions()
   rv = FixupURLFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = FrecencyNotificationFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = StoreLastInsertedIdFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = HashFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
+  rv = GetQueryParamFunction::create(mMainConn);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
 Database::InitTempEntities()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/toolkit/components/places/SQLFunctions.cpp
+++ b/toolkit/components/places/SQLFunctions.cpp
@@ -1,14 +1,15 @@
 /* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/storage.h"
+#include "mozilla/dom/URLSearchParams.h"
 #include "nsString.h"
 #include "nsUnicharUtils.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsEscape.h"
 #include "mozIPlacesAutoComplete.h"
 #include "SQLFunctions.h"
 #include "nsMathUtils.h"
 #include "nsUnicodeProperties.h"
@@ -241,16 +242,41 @@ namespace {
     uint32_t len;
     const char* str = aValues->AsSharedUTF8String(aIndex, &len);
     if (!str) {
       return nsDependentCString("", (uint32_t)0);
     }
     return nsDependentCString(str, len);
   }
 
+  class MOZ_STACK_CLASS GetQueryParamIterator final :
+    public URLParams::ForEachIterator
+  {
+  public:
+    explicit GetQueryParamIterator(const nsCString& aParamName,
+                                   nsVariant* aResult)
+      : mParamName(aParamName)
+      , mResult(aResult)
+    {}
+
+    bool URLParamsIterator(const nsAString& aName,
+                           const nsAString& aValue) override
+    {
+      NS_ConvertUTF16toUTF8 name(aName);
+      if (!mParamName.Equals(name)) {
+        return true;
+      }
+      mResult->SetAsAString(aValue);
+      return false;
+    }
+  private:
+    const nsCString& mParamName;
+    nsVariant* mResult;
+  };
+
 } // End anonymous namespace
 
 namespace mozilla {
 namespace places {
 
 ////////////////////////////////////////////////////////////////////////////////
 //// AutoComplete Matching Function
 
@@ -1002,16 +1028,54 @@ namespace places {
     RefPtr<nsVariant> result = new nsVariant();
     rv = result->SetAsInt64(lastInsertedId);
     NS_ENSURE_SUCCESS(rv, rv);
     result.forget(_result);
     return NS_OK;
   }
 
 ////////////////////////////////////////////////////////////////////////////////
+//// Get Query Param Function
+
+  /* static */
+  nsresult
+  GetQueryParamFunction::create(mozIStorageConnection *aDBConn)
+  {
+    RefPtr<GetQueryParamFunction> function = new GetQueryParamFunction();
+    return aDBConn->CreateFunction(
+      NS_LITERAL_CSTRING("get_query_param"), 2, function
+    );
+  }
+
+  NS_IMPL_ISUPPORTS(
+    GetQueryParamFunction,
+    mozIStorageFunction
+  )
+
+  NS_IMETHODIMP
+  GetQueryParamFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
+                                        nsIVariant **_result)
+  {
+    // Must have non-null function arguments.
+    MOZ_ASSERT(aArguments);
+
+    nsDependentCString queryString = getSharedUTF8String(aArguments, 0);
+    nsDependentCString paramName = getSharedUTF8String(aArguments, 1);
+
+    RefPtr<nsVariant> result = new nsVariant();
+    if (!queryString.IsEmpty() && !paramName.IsEmpty()) {
+      GetQueryParamIterator iterator(paramName, result);
+      URLParams::Parse(queryString, iterator);
+    }
+
+    result.forget(_result);
+    return NS_OK;
+  }
+
+////////////////////////////////////////////////////////////////////////////////
 //// Hash Function
 
   /* static */
   nsresult
   HashFunction::create(mozIStorageConnection *aDBConn)
   {
     RefPtr<HashFunction> function = new HashFunction();
     return aDBConn->CreateFunction(
--- a/toolkit/components/places/SQLFunctions.h
+++ b/toolkit/components/places/SQLFunctions.h
@@ -411,12 +411,41 @@ public:
    * @param aDBConn
    *        The database connection to register with.
    */
   static nsresult create(mozIStorageConnection *aDBConn);
 private:
   ~HashFunction() {}
 };
 
+////////////////////////////////////////////////////////////////////////////////
+//// Get Query Param Function
+
+/**
+ * Extracts and returns the value of a parameter from a query string.
+ *
+ * @param string
+ *        A string.
+ * @return
+ *        The value of the query parameter as a string, or NULL if not set.
+ */
+class GetQueryParamFunction final : public mozIStorageFunction
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_MOZISTORAGEFUNCTION
+
+  /**
+   * Registers the function with the specified database connection.
+   *
+   * @param aDBConn
+   *        The database connection to register with.
+   */
+  static nsresult create(mozIStorageConnection *aDBConn);
+private:
+  ~GetQueryParamFunction() {}
+};
+
+
 } // namespace places
 } // namespace mozilla
 
 #endif // mozilla_places_SQLFunctions_h_
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/unit/test_get_query_param_sql_function.js
@@ -0,0 +1,19 @@
+add_task(async function test_get_query_param_sql_function() {
+  let db = await PlacesUtils.promiseDBConnection();
+  await Assert.rejects(db.execute(`SELECT get_query_param()`),
+                       /wrong number of arguments/);
+  let rows = await db.execute(`SELECT
+    get_query_param('a=b&c=d', 'a'),
+    get_query_param('a=b&c=d', 'c'),
+    get_query_param('a=b&a=c', 'a'),
+    get_query_param('a=b&c=d', 'e'),
+    get_query_param('a', 'a'),
+    get_query_param(NULL, NULL),
+    get_query_param('a=b&c=d', NULL),
+    get_query_param(NULL, 'a')`);
+  let results = ["b", "d", "b", null, "", null, null, null];
+  equal(rows[0].numEntries, results.length);
+  for (let i = 0; i < results.length; ++i) {
+    equal(rows[0].getResultByIndex(i), results[i]);
+  }
+});
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -107,8 +107,9 @@ skip-if = (os == "win" && os_version == 
 [test_tag_autocomplete_search.js]
 [test_tagging.js]
 [test_telemetry.js]
 [test_update_frecency_after_delete.js]
 [test_utils_backups_create.js]
 [test_utils_getURLsForContainerNode.js]
 [test_utils_setAnnotationsForItem.js]
 [test_visitsInDB.js]
+[test_get_query_param_sql_function.js]