Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into latest
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickebates committed Sep 24, 2014
2 parents f3e5716 + 2b23bbe commit b8dfc70
Show file tree
Hide file tree
Showing 7 changed files with 2,108 additions and 10 deletions.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Project Nami
===============

###Version: `0.9.18`###
###Version: `0.10.0`###

###Description:###

Expand All @@ -26,13 +26,15 @@ The short answer here is, **WordPress doesn't work with MSSQL**. It's a sad stor

While I would agree with all of this, writing ( or using ) a custom `wp-db.php` drop-in is not enough to get WordPress running on MSSQL. In reality, WP Core is littered with MySQL queries, which means a custom db class won't cover all your bases. WP will remain broken and unsuable.

So, what about using a translation plugin? This can work, but it's hardly optimal. Every query that comes in needs to be parsed and converted to MSSQL style syntax before it's executed. **Yikes!**
So, what about using only a translation plugin? This can work, but it's hardly optimal. Every query that comes in needs to be parsed and converted to MSSQL style syntax before it's executed. **Yikes!**

We needed a version of WordPress powered by MSSQL in the cloud on Windows Azure. So, we rewrote WP Core to do this very thing. Forking WordPress may seem extreme, but the software simply isn't to the point where true database abstraction is feasible. Maybe someday it will be. In the meantime, Project Nami is an alternative. :)

###A few things to note:###
* Project Nami will work with any WordPress plugins/themes that utilize WordPress specific APIs. However, custom SQL queries will most likely fail if they use MySQL based syntax. In most cases, these issues can be easily resolved by using a WordPress API. If you absolutely have to use custom SQL, make sure it's MSSQL Server 2012 compliant. **We highly recommend using WordPress APIs everywhere you can.** Among other things, it makes your code portable across Project Nami and WordPress.

* As of version 0.10.0, a fallback translation layer has been added to help with MySQL-specific syntax used by plugins.

* Project Nami requires ***MSSQL Server 2012*** in order to function properly. Until this version was released, there wasn't really an MSSQL native method of handling the MySQL `LIMIT` when using an offset. However, `OFFSET FETCH` can now be used in conjunction with an MSSQL `ORDER BY` to achieve the equivalent of a MySQL `LIMIT` with an offset.

* Due to the use of the `sqlsrv` PHP extension, Project Nami will only run on Windows, at the moment.
14 changes: 11 additions & 3 deletions license.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
WordPress - Web publishing software
Project Nami - Web publishing software for the Windows Azure platform

Copyright 2014 by the contributors
Copyright 2013-2014 by the contributors

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -31,10 +31,18 @@ and

WordPress - Web publishing software

Copyright 2003-2010 by the contributors
Copyright 2003-2014 by the contributors

WordPress is released under the GPL

and

WP Database Abstraction - DB abstraction plugin for WordPress

Contribitors: omniti, A.Garcia & A.Gentile

WP Database Abstraction is released under the GPL

=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

GNU GENERAL PUBLIC LICENSE
Expand Down
243 changes: 243 additions & 0 deletions wp-includes/fields_map.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
<?php
/**
* Fields mapping
*
* Some column types from MySQL
* don't have an exact equivalent for SQL Server
* That is why we need to know what column types they were
* originally to make the translations.
*
* original authors A.Garcia & A.Gentile
* */
class Fields_map
{
var $fields_map = array();
var $filepath = '';

/**
* php4 style call to constructor.
*
* @since 2.7.1
*
*/
function Fields_map($blogid = null) {
return $this->__construct($blogid = null);
}

/**
* Set filepath
*
* PHP5 style constructor for compatibility with PHP5.
*
* @since 2.7.1
*/
function __construct($blogid = null) {
/* Allows a directory for all field maps parsed types files */
if (defined('DB_CACHE_LOCATION')) {
$loc = DB_CACHE_LOCATION . '/';
} else {
$loc = '';
}

if (!is_null($blogid)) {
$blog_filepath = trim(str_replace('mu-plugins/wp-db-abstraction/translations/sqlsrv', '', strtr(dirname(__FILE__), '\\', '/')), '/') . $loc . '/fields_map.parsed_types.' . $blogid . '.php';

// if the file doesn't exist, we're going to grab our default file, read it in to get the "base" tables, then set our filepath differently again
if (!file_exists($blog_filepath)) {
$this->filepath = trim(str_replace('mu-plugins/wp-db-abstraction/translations/sqlsrv', '', strtr(dirname(__FILE__), '\\', '/')), '/') . $loc . '/fields_map.parsed_types.php';
$this->read();
$this->filepath = $blog_filepath;
$this->update_for('');
}

$this->filepath = $blog_filepath;
} else {
$this->filepath = trim(str_replace('mu-plugins/wp-db-abstraction/translations/sqlsrv', '', strtr(dirname(__FILE__), '\\', '/')), '/') . $loc . '/fields_map.parsed_types.php';

// if the file doesn't exist yet, we'll try to write it out and blow up if we can't
if (!file_exists($this->filepath)) {
$this->update_for('');
}
}
}

/**
* Get array of fields by type from fields_map property
*
* @since 2.8
* @param $type
* @param $table
*
* @return array
*/
function by_type($type, $table = null) {
$ret = array();
foreach ($this->fields_map as $tables => $fields) {
if ( is_array($fields) ) {
foreach ($fields as $field_name => $field_meta) {
if ( $field_meta['type'] == $type ) {
if (is_null($table) || $tables == $table) {
$ret[] = $field_name;
}
}
}
}
}
return $ret;
}

/**
* Get array of tables from fields_map property
*
* @since 2.8
*
* @return array
*/
function get_tables() {
$ret = array();
foreach ($this->fields_map as $tables => $fields) {
$ret[] = $tables;
}
return $ret;
}

/**
* Given a query find the column types
*
* @since 2.8
* @param $qry
*
* @return array
*/
function extract_column_types($qry) {
//table name
$matches = array();
if (preg_match('/[CREATE|ALTER] TABLE (.*) \(/i',$qry,$matches)){
$table_name = $matches[1];
} else {
$table_name = '';
}


$fields_and_indexes = substr($qry,strpos($qry,'(')+1,strrpos($qry,')')-(strpos($qry,'(')+1));
$just_fields = trim(substr($fields_and_indexes,0,$this->index_pos($fields_and_indexes)));

$field_lines = explode(',',$just_fields);
$field_types = array();
foreach ($field_lines as $field_line) {
if (!empty($field_line)){
$field_line = trim($field_line);
$words = explode(' ',$field_line,3);
$first_word = $words[0];
$field_type = $this->type_translations($words[1]);
if ($field_type !== false) {
$field_types[$first_word] = array('type'=>$field_type);
}
}
}

//get primary key
$just_indexes = trim(substr($fields_and_indexes,$this->index_pos($fields_and_indexes)));
$matches = array();
$has_primary_key = preg_match('/PRIMARY KEY *\((.*?)[,|\)]/i',$just_indexes,$matches);
if ($has_primary_key) {
$primary_key = trim($matches[1]);
$field_types[$primary_key] = array('type' => 'primary_id');
}
ksort($field_types);

return array($table_name => $field_types);
}

/**
* According to the column types in MySQL
*
* @since 2.8
* @param $field_type
*
* @return array
*/
function type_translations($field_type) {
//false means not translate this field.
$translations = array(
array('pattern' => '/varchar(.*)/', 'trans' => 'nvarchar'),
array('pattern' => '/.*text.*/', 'trans' => 'nvarchar'),
array('pattern' => '/.*datetime.*/','trans' => 'date'),
array('pattern' => '/[big|medium|tiny]*int(.*)/', 'trans' => 'int'),
);

$res = '';
while (($res === '') && ($trans = array_shift($translations))) {
if (preg_match($trans['pattern'],$field_type)) {
$res = $trans['trans'];
}
}

if ($res === '') {
$res = $field_type;
}
return $res;
}


/**
* Get array of tables from fields_map property
*
* @since 2.8
* @param $fields_and_indexes
*
* @return array
*/
function index_pos($fields_and_indexes) {
$reserved_words = array('PRIMARY KEY', 'UNIQUE');
$res = false;
while (($res === false) && ($reserved_word = array_shift($reserved_words))){
$res = stripos($fields_and_indexes,$reserved_word);
}

return $res;
}

/**
* Update fields may given a CREATE | ALTER query
*
* @since 2.8
* @param $qry
*
* @return array
*/
function update_for($qry) {
$this->read();
$this->fields_map = array_merge($this->fields_map, $this->extract_column_types($qry));
$worked = file_put_contents($this->filepath, '<?php return ' . var_export($this->fields_map, true) . "\n ?>");
if (false === $worked) {
// two directories up is our error page
$wp_db_ab_plugin_path = realpath(dirname(__FILE__) . DIRECTORY_SEPARATOR
. '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$error_message = 'WP Database Abstraction must write to the wp-content/fields_map.parsed_types.php file located at ' . $this->filepath .
'Either the file does not exist, or the webserver cannot write to the file';
include $wp_db_ab_plugin_path . 'error_page.php';
die;
}
return $this->fields_map;
}

/**
* Get the fields_map from memory or from the file.
*
* @since 2.8
*
* @return array
*/
function read() {
if (empty($this->fields_map)) {
if (file_exists($this->filepath)) {
$this->fields_map = require($this->filepath);
} else {
$this->fields_map = array();
}
}
return $this->fields_map;
}

}
2 changes: 1 addition & 1 deletion wp-includes/option.php
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ function add_option( $option, $value = '', $deprecated = '', $autoload = 'yes' )
do_action( 'add_option', $option, $value );

$result = $wpdb->query( $wpdb->prepare( "IF NOT EXISTS (SELECT * FROM [$wpdb->options] WHERE [option_name] = '%s') INSERT INTO [$wpdb->options] ([option_name], [option_value], [autoload]) VALUES ('%s', '%s', '%s') else UPDATE [$wpdb->options] set [option_value] = '%s', [autoload] = '%s' where [option_name] = '%s'", array( $option, $option, $serialized_value, $autoload, $serialized_value, $autoload, $option ) ) );
if ( ! $result )
if ( $result === false )
return false;

if ( ! defined( 'WP_INSTALLING' ) ) {
Expand Down
2 changes: 1 addition & 1 deletion wp-includes/pn-version.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?php

$pn_version = '0.9.18';
$pn_version = '0.10.0';

?>
Loading

0 comments on commit b8dfc70

Please sign in to comment.